<?php

namespace App\Service;

use App\Repository\ConcessionRepository;
use Doctrine\ORM\EntityManagerInterface;

/**
 * Convertit les concessions stockées par NOM (héritage d'avant le référentiel à IDs)
 * vers leur ID externe : périmètres utilisateurs, snapshots des inventaires et photo
 * du stock. Idempotent (les IDs déjà en place sont conservés, les noms sans
 * correspondance dans le référentiel restent tels quels) ; appelé après chaque
 * import du référentiel dans Paramètres.
 *
 * SQL direct : évite d'hydrater les ScanSession (le JSON `results` peut être volumineux).
 */
final class ConcessionIdMigrator
{
    public function __construct(
        private readonly EntityManagerInterface $em,
        private readonly ConcessionRepository $concessions,
    ) {
    }

    /**
     * @return array{users: int, sessions: int, stock: int} Nombre de lignes converties par table
     */
    public function migrate(): array
    {
        $ids = $this->concessions->allByExternalId();
        $nameToId = $this->concessions->normalizedNameToId();
        if ([] === $ids) {
            return ['users' => 0, 'sessions' => 0, 'stock' => 0];
        }

        $conn = $this->em->getConnection();

        $users = 0;
        foreach ($conn->fetchAllAssociative('SELECT id, concessions FROM users') as $row) {
            $converted = $this->convertList((string) $row['concessions'], $ids, $nameToId);
            if (null !== $converted) {
                $conn->executeStatement('UPDATE users SET concessions = ? WHERE id = ?', [$converted, $row['id']]);
                ++$users;
            }
        }

        $sessions = 0;
        foreach ($conn->fetchAllAssociative('SELECT id, concessions FROM scan_sessions') as $row) {
            $converted = $this->convertList((string) $row['concessions'], $ids, $nameToId);
            if (null !== $converted) {
                $conn->executeStatement('UPDATE scan_sessions SET concessions = ? WHERE id = ?', [$converted, $row['id']]);
                ++$sessions;
            }
        }

        $stock = 0;
        foreach ($conn->fetchAllAssociative('SELECT id, concession_id FROM stock_vehicles') as $row) {
            $value = (string) $row['concession_id'];
            $new = $this->convertValue($value, $ids, $nameToId);
            if ($new !== $value) {
                $conn->executeStatement('UPDATE stock_vehicles SET concession_id = ? WHERE id = ?', [$new, $row['id']]);
                ++$stock;
            }
        }

        return ['users' => $users, 'sessions' => $sessions, 'stock' => $stock];
    }

    /**
     * Convertit une colonne JSON de concessions ; null si rien à changer.
     *
     * @param array<string, string> $ids
     * @param array<string, string> $nameToId
     */
    private function convertList(string $json, array $ids, array $nameToId): ?string
    {
        $list = json_decode($json, true);
        if (!\is_array($list)) {
            return null;
        }

        $changed = false;
        $out = [];
        foreach ($list as $value) {
            if (!\is_string($value)) {
                continue;
            }
            $new = $this->convertValue($value, $ids, $nameToId);
            $changed = $changed || $new !== $value;
            $out[] = $new;
        }

        return $changed ? json_encode(array_values(array_unique($out)), JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) : null;
    }

    /**
     * @param array<string, string> $ids
     * @param array<string, string> $nameToId
     */
    private function convertValue(string $value, array $ids, array $nameToId): string
    {
        if (isset($ids[$value])) {
            return $value; // déjà un ID
        }

        return $nameToId[StockImporter::normalize($value)] ?? $value; // nom → ID, sinon inchangé
    }
}
