<?php

declare(strict_types=1);

final class Asm3MigrationPlanner
{
    public static function plan(string $path, PDO $db): array
    {
        $source = Asm3Analyzer::rows($path, ['animal', 'species', 'basecolour', 'lksex']);
        $species = self::lookup($source['species'] ?? [], 'ID', 'SPECIESNAME');
        $colors = self::lookup($source['basecolour'] ?? [], 'ID', 'BASECOLOUR');
        $sexes = self::lookup($source['lksex'] ?? [], 'ID', 'SEX');
        $existing = $db
            ->query('SELECT id,name,internal_code,chip_id,birth_date FROM animals')
            ->fetchAll(PDO::FETCH_ASSOC);
        $byChip = [];
        $byCode = [];
        $byIdentity = [];
        $byName = [];
        foreach ($existing as $animal) {
            $chip = self::identifier($animal['chip_id'] ?? '');
            if ($chip !== '') {
                $byChip[$chip][] = $animal;
            }
            $code = self::text($animal['internal_code'] ?? '');
            if ($code !== '') {
                $byCode[$code][] = $animal;
            }
            $identity = self::identity($animal['name'] ?? '', $animal['birth_date'] ?? '');
            if ($identity !== '') {
                $byIdentity[$identity][] = $animal;
            }
            $name = self::text($animal['name'] ?? '');
            if ($name !== '') {
                $byName[$name][] = $animal;
            }
        }
        $items = [];
        $summary = ['create' => 0, 'match' => 0, 'review' => 0];
        foreach ($source['animal'] ?? [] as $row) {
            $mapped = self::mapAnimal($row, $species, $colors, $sexes);
            $candidates = [];
            $reasons = [];
            $chip = self::identifier($mapped['chip_id']);
            if ($chip !== '' && !empty($byChip[$chip])) {
                foreach ($byChip[$chip] as $a) {
                    $candidates[(int) $a['id']] = $a;
                }
                $reasons[] = 'même identification';
            }
            // Les codes refuge peuvent être réutilisés après une remise à zéro d’ASM3.
            // Un code seul n’est donc jamais une preuve suffisante entre deux sauvegardes historiques.
            $code = self::text($mapped['internal_code']);
            $identity = self::identity($mapped['name'], $mapped['birth_date']);
            if ($identity !== '' && !empty($byIdentity[$identity])) {
                foreach ($byIdentity[$identity] as $a) {
                    $candidates[(int) $a['id']] = $a;
                }
                $reasons[] = 'même nom et naissance';
            }
            $strongMatch = count($candidates) > 0;
            if (!$strongMatch) {
                $name = self::text($mapped['name']);
                if ($name !== '' && !empty($byName[$name])) {
                    $weakByName = $byName[$name];
                    $knownDifferentBirths = $mapped['birth_date'] !== '' && $mapped['birth_date'] !== null;
                    foreach ($weakByName as $a) {
                        $knownDifferentBirths =
                            $knownDifferentBirths &&
                            !empty($a['birth_date']) &&
                            $a['birth_date'] !== $mapped['birth_date'];
                    }
                    if (!$knownDifferentBirths) {
                        foreach ($weakByName as $a) {
                            $candidates[(int) $a['id']] = $a;
                        }
                        $reasons[] = 'même nom uniquement';
                    }
                }
            }
            // Un code isolé et réutilisé ne doit ni fusionner, ni bloquer la création.
            // Asm3AnimalImporter suffixera le code technique si nécessaire.
            if (!$strongMatch && count($candidates) === 0) {
                $action = 'create';
            } elseif ($strongMatch && count($candidates) === 1) {
                $action = 'match';
            } else {
                $action = 'review';
            }
            $summary[$action]++;
            $items[] = [
                'asm3_id' => (int) ($row['ID'] ?? 0),
                'action' => $action,
                'match_reasons' => array_values(array_unique($reasons)),
                'globinours_candidates' => array_values(
                    array_map(
                        static fn($a) => ['id' => (int) $a['id'], 'name' => $a['name'], 'code' => $a['internal_code']],
                        $candidates,
                    ),
                ),
                'mapped' => $mapped,
            ];
        }
        return [
            'source' => basename($path),
            'generated_at' => date(DATE_ATOM),
            'read_only' => true,
            'summary' => $summary,
            'items' => $items,
            'lookups' => ['species' => $species, 'colors' => $colors, 'sexes' => $sexes],
        ];
    }

    private static function mapAnimal(array $r, array $species, array $colors, array $sexes): array
    {
        $date = static fn($v) => $v && preg_match('/^\d{4}-\d{2}-\d{2}/', (string) $v, $m) ? $m[0] : null;
        $yes = static fn($v) => (string) $v === '1';
        $tri = static fn($v) => match ((string) $v) {
            '0' => 'no',
            '1' => 'yes',
            default => 'unknown',
        };
        $chip = trim((string) ($r['IDENTICHIPNUMBER'] ?? ''));
        $tattoo = trim((string) ($r['TATTOONUMBER'] ?? ''));
        $identifier = $chip !== '' ? $chip : $tattoo;
        $deceased = $date($r['DECEASEDDATE'] ?? null) !== null || $yes($r['PUTTOSLEEP'] ?? 0);
        $availability = $yes($r['ISNOTAVAILABLEFORADOPTION'] ?? 0)
            ? 'not_available'
            : ($yes($r['ADOPTABLE'] ?? 0)
                ? 'available'
                : 'unknown');
        return [
            'name' => trim((string) ($r['ANIMALNAME'] ?? '')),
            'internal_code' => trim((string) ($r['SHELTERCODE'] ?? '' ?: $r['SHORTCODE'] ?? '')),
            'species' => self::species($species[(string) ($r['SPECIESID'] ?? '')] ?? ''),
            'sex' => match (mb_strtolower((string) ($sexes[(string) ($r['SEX'] ?? '')] ?? ''))) {
                'mâle' => 'M',
                'femelle' => 'F',
                default => 'U',
            },
            'birth_date' => $date($r['DATEOFBIRTH'] ?? null),
            'birth_is_estimated' => $yes($r['ESTIMATEDDOB'] ?? 0) ? 1 : 0,
            'breed' => trim((string) ($r['BREEDNAME'] ?? '')),
            'color' => $colors[(string) ($r['BASECOLOURID'] ?? '')] ?? null,
            'notes' => trim((string) ($r['ANIMALCOMMENTS'] ?? '')),
            'intake_date' => $date($r['MOSTRECENTENTRYDATE'] ?? null ?: $r['DATEBROUGHTIN'] ?? null),
            'status' => $deceased ? 'decede' : ($yes($r['ISQUARANTINE'] ?? 0) ? 'quarantaine' : 'refuge'),
            'archived' => $yes($r['ARCHIVED'] ?? 0),
            'deceased_date' => $date($r['DECEASEDDATE'] ?? null),
            'identification_type' => $chip !== '' ? 'microchip' : ($tattoo !== '' ? 'tattoo' : 'unknown'),
            'chip_id' => $identifier,
            'identification_date' => $date($chip !== '' ? $r['IDENTICHIPDATE'] ?? null : $r['TATTOODATE'] ?? null),
            'identification_registration_status' => match ((string) ($r['IDENTICHIPSTATUS'] ?? '')) {
                '1' => 'registered',
                '0' => 'pending',
                default => 'unknown',
            },
            'sterilization_status' => $yes($r['NEUTERED'] ?? 0) ? 'yes' : 'unknown',
            'sterilization_date' => $date($r['NEUTEREDDATE'] ?? null),
            'adoption_availability' => $availability,
            'adoption_available_from' => $date($r['HOLDUNTILDATE'] ?? null),
            'adoption_unavailability_reason' => trim((string) ($r['REASONNO'] ?? '')),
            'compatibility_dogs' => $tri($r['ISGOODWITHDOGS'] ?? null),
            'compatibility_cats' => $tri($r['ISGOODWITHCATS'] ?? null),
            'compatibility_children' => $tri($r['ISGOODWITHCHILDREN'] ?? null),
            'house_trained' => $tri($r['ISHOUSETRAINED'] ?? null),
        ];
    }
    private static function lookup(array $rows, string $key, string $value): array
    {
        $out = [];
        foreach ($rows as $row) {
            $out[(string) $row[$key]] = (string) $row[$value];
        }
        return $out;
    }
    private static function species(string $value): string
    {
        return match (mb_strtolower($value)) {
            'chat' => 'chat',
            'chien' => 'chien',
            default => mb_strtolower($value) ?: 'inconnu',
        };
    }
    private static function identifier(mixed $value): string
    {
        return strtoupper((string) preg_replace('/[^a-zA-Z0-9]/', '', (string) $value));
    }
    private static function text(mixed $value): string
    {
        return mb_strtolower(trim((string) $value));
    }
    private static function identity(mixed $name, mixed $birth): string
    {
        $name = self::text($name);
        $birth = trim((string) $birth);
        return $name !== '' && $birth !== '' ? $name . '|' . $birth : '';
    }
}
