<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Service\Import;

use League\Csv\Reader;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
 * Load French's postal codes from opendata.
 *
 * Currently, the source is datanova / la poste:
 * https://datanova.legroupe.laposte.fr/explore/dataset/laposte_hexasmal/information/
 */
class PostalCodeFRFromOpenData
{
    private const CSV = 'https://datanova.laposte.fr/data-fair/api/v1/datasets/laposte-hexasmal/metadata-attachments/base-officielle-codes-postaux.csv';

    public function __construct(private readonly PostalCodeBaseImporter $baseImporter, private readonly HttpClientInterface $client, private readonly LoggerInterface $logger) {}

    public function import(): void
    {
        $response = $this->client->request('GET', self::CSV);

        if (200 !== $response->getStatusCode()) {
            throw new \RuntimeException('could not download CSV');
        }

        $tmpfile = tmpfile();

        if (false === $tmpfile) {
            throw new \RuntimeException('could not create temporary file');
        }

        foreach ($this->client->stream($response) as $chunk) {
            fwrite($tmpfile, $chunk->getContent());
        }

        fseek($tmpfile, 0);

        $csv = Reader::from($tmpfile);
        $csv->setDelimiter(',');
        $csv->setHeaderOffset(0);

        foreach ($csv as $offset => $record) {
            $this->handleRecord($record);
        }

        $this->baseImporter->finalize();
        fclose($tmpfile);

        $this->logger->info(self::class.' postal code fetched', ['offset' => $offset ?? 0]);
    }

    private function handleRecord(array $record): void
    {
        if ('' !== trim((string) $record['_geopoint'])) {
            [$lat, $lon] = array_map(static fn ($el) => (float) trim($el), explode(',', (string) $record['_geopoint']));
        } else {
            $lat = $lon = 0.0;
        }

        $ref = trim((string) $record['code_commune_insee']);

        if (str_starts_with($ref, '987')) {
            // some differences in French Polynesia
            $ref .= '.'.trim((string) $record['libelle_d_acheminement']);
        }

        $this->baseImporter->importCode(
            'FR',
            trim((string) $record['libelle_d_acheminement']),
            trim((string) $record['code_postal']),
            $ref,
            'INSEE',
            $lat,
            $lon,
            4326
        );
    }
}
