<?php

declare(strict_types=1);

final class Asm3MovementImporter
{
    public static function run(string $path, PDO $db, bool $apply = false, bool $createBackup = true): array
    {
        $hash = hash_file('sha256', $path);
        if ($hash === false) {
            throw new RuntimeException(t('asm3.hash_unavailable'));
        }
        $rows = Asm3Analyzer::rows($path, ['adoption'])['adoption'] ?? [];
        $events = [];
        $unsupported = 0;
        foreach ($rows as $row) {
            $source = (int) ($row['ID'] ?? 0);
            $type = (int) ($row['MOVEMENTTYPE'] ?? 0);
            $movementDate = self::date($row['MOVEMENTDATE'] ?? null);
            $returnDate = self::date($row['RETURNDATE'] ?? null);
            $reservationDate = self::date($row['RESERVATIONDATE'] ?? null);
            $cancelDate = self::date($row['RESERVATIONCANCELLEDDATE'] ?? null);
            if ($reservationDate) {
                $events[] = self::event($row, $source, 'reservation', $reservationDate, 'Réservation ASM3');
            }
            if ($cancelDate) {
                $events[] = self::event($row, $source, 'cancellation', $cancelDate, 'Annulation de réservation ASM3');
            }
            if ($movementDate && $type === 1) {
                $events[] = self::event($row, $source, 'adoption', $movementDate, 'Adoption définitive ASM3');
            } elseif ($movementDate && in_array($type, [2, 12], true)) {
                $events[] = self::event(
                    $row,
                    $source,
                    'foster',
                    $movementDate,
                    $type === 12 || (string) ($row['ISPERMANENTFOSTER'] ?? 0) === '1'
                        ? 'FA permanente ASM3'
                        : 'Famille d’accueil ASM3',
                );
            } elseif ($movementDate && !in_array($type, [0, 9, 10], true)) {
                $unsupported++;
            }
            if ($returnDate && in_array($type, [1, 2, 12], true)) {
                $events[] = self::event(
                    $row,
                    $source,
                    'return',
                    $returnDate,
                    trim((string) ($row['REASONFORRETURN'] ?? '')) ?: 'Retour enregistré dans ASM3',
                );
            }
        }
        $links = self::links($db, $hash);
        $summary = [
            'events' => count($events),
            'adoptions' => 0,
            'fosters' => 0,
            'returns' => 0,
            'reservations' => 0,
            'cancellations' => 0,
            'unresolved_animals' => 0,
            'unresolved_contacts' => 0,
            'unsupported_movements' => $unsupported,
            'would_import' => 0,
            'created' => 0,
            'already_imported' => 0,
        ];
        foreach ($events as $event) {
            $summary[
                $event['type'] === 'adoption'
                    ? 'adoptions'
                    : ($event['type'] === 'foster'
                        ? 'fosters'
                        : ($event['type'] === 'return'
                            ? 'returns'
                            : ($event['type'] === 'reservation'
                                ? 'reservations'
                                : 'cancellations')))
            ]++;
            if (!isset($links['animal'][$event['animal_source_id']])) {
                $summary['unresolved_animals']++;
                continue;
            }
            if ($event['contact_source_id'] > 0 && !isset($links['contact'][$event['contact_source_id']])) {
                $summary['unresolved_contacts']++;
            }
            $summary['would_import']++;
        }
        if (!$apply) {
            return ['mode' => 'dry-run', 'read_only' => true, 'summary' => $summary];
        }
        if ($createBackup) {
            $backup = BackupService::create('pre-asm3-import');
            $backupName = $backup['name'] ?? null;
        } else {
            $backupName = null;
        }
        $ownsTransaction = !$db->inTransaction();
        if ($ownsTransaction) {
            $db->beginTransaction();
        }
        try {
            $db->prepare(
                "INSERT INTO asm3_import_runs(source_name,source_sha256,status,summary_json,backup_name) VALUES(:name,:hash,'completed','{}',:backup)",
            )->execute([':name' => basename($path) . ' · mouvements', ':hash' => $hash, ':backup' => $backupName]);
            $runId = (int) $db->lastInsertId();
            $affected = [];
            foreach ($events as $event) {
                $animalId = $links['animal'][$event['animal_source_id']] ?? 0;
                if (!$animalId) {
                    continue;
                }
                $affected[$animalId] = true;
                $entity = 'placement-' . $event['type'];
                $exists = $db->prepare(
                    'SELECT target_id FROM asm3_import_links WHERE source_sha256=:hash AND entity_type=:entity AND source_id=:source',
                );
                $exists->execute([':hash' => $hash, ':entity' => $entity, ':source' => $event['source_id']]);
                if ($exists->fetchColumn()) {
                    $summary['already_imported']++;
                    continue;
                }
                $contactId = $links['contact'][$event['contact_source_id']] ?? null;
                $adoptionId = null;
                if ($event['type'] === 'adoption') {
                    $contact = self::contact($db, $contactId);
                    $db->prepare(
                        'INSERT INTO adoptions(animal_id,adopter_name,adopter_phone,adopter_email,adopter_address,adopter_postal_code,adopter_city,adopter_country,adoption_date,notes,adopter_contact_id) VALUES(:animal,:name,:phone,:email,:address,:postal,:city,:country,:date,:notes,:contact)',
                    )->execute([
                        ':animal' => $animalId,
                        ':name' => $contact['name'] ?? 'Adoptant ASM3 non retrouvé',
                        ':phone' => $contact['phone'] ?? null,
                        ':email' => $contact['email'] ?? null,
                        ':address' => $contact['address'] ?? null,
                        ':postal' => $contact['postal_code'] ?? null,
                        ':city' => $contact['city'] ?? null,
                        ':country' => $contact['country'] ?? 'France',
                        ':date' => $event['date'],
                        ':notes' => $event['notes'] ?: null,
                        ':contact' => $contactId,
                    ]);
                    $adoptionId = (int) $db->lastInsertId();
                }
                $db->prepare(
                    'INSERT INTO animal_placements(animal_id,event_type,event_date,contact_id,reason,notes,adoption_id) VALUES(:animal,:type,:date,:contact,:reason,:notes,:adoption)',
                )->execute([
                    ':animal' => $animalId,
                    ':type' => $event['type'],
                    ':date' => $event['date'],
                    ':contact' => $contactId,
                    ':reason' => $event['reason'],
                    ':notes' => $event['notes'] ?: null,
                    ':adoption' => $adoptionId,
                ]);
                $placementId = (int) $db->lastInsertId();
                $db->prepare(
                    'INSERT INTO asm3_import_links(source_sha256,entity_type,source_id,target_id,import_run_id) VALUES(:hash,:entity,:source,:target,:run)',
                )->execute([
                    ':hash' => $hash,
                    ':entity' => $entity,
                    ':source' => $event['source_id'],
                    ':target' => $placementId,
                    ':run' => $runId,
                ]);
                $summary['created']++;
            }
            self::syncAnimalStates($db, array_keys($affected));
            $db->prepare('UPDATE asm3_import_runs SET summary_json=:summary WHERE id=:id')->execute([
                ':summary' => json_encode($summary, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
                ':id' => $runId,
            ]);
            if ($ownsTransaction) {
                $db->commit();
            }
        } catch (Throwable $e) {
            if ($ownsTransaction && $db->inTransaction()) {
                $db->rollBack();
            }
            throw $e;
        }
        return ['mode' => 'apply', 'read_only' => false, 'backup' => $backupName, 'summary' => $summary];
    }
    private static function event(array $row, int $source, string $type, string $date, string $reason): array
    {
        return [
            'source_id' => $source,
            'type' => $type,
            'date' => $date,
            'animal_source_id' => (int) ($row['ANIMALID'] ?? 0),
            'contact_source_id' => (int) ($row['OWNERID'] ?? 0),
            'reason' => $reason,
            'notes' => trim((string) ($row['COMMENTS'] ?? '')),
        ];
    }
    private static function date(mixed $value): ?string
    {
        return $value && preg_match('/^\d{4}-\d{2}-\d{2}/', (string) $value, $m) ? $m[0] : null;
    }
    private static function links(PDO $db, string $hash): array
    {
        $out = ['animal' => [], 'contact' => []];
        $s = $db->prepare(
            "SELECT entity_type,source_id,target_id FROM asm3_import_links WHERE source_sha256=:hash AND entity_type IN ('animal','contact')",
        );
        $s->execute([':hash' => $hash]);
        foreach ($s as $r) {
            $out[$r['entity_type']][(int) $r['source_id']] = (int) $r['target_id'];
        }
        return $out;
    }
    private static function contact(PDO $db, ?int $id): ?array
    {
        if (!$id) {
            return null;
        }
        $s = $db->prepare('SELECT * FROM directory_contacts WHERE id=:id');
        $s->execute([':id' => $id]);
        return $s->fetch(PDO::FETCH_ASSOC) ?: null;
    }
    private static function syncAnimalStates(PDO $db, array $animalIds): void
    {
        $latest = $db->prepare(
            'SELECT ap.*,dc.name contact_name,dc.address,dc.postal_code,dc.city FROM animal_placements ap LEFT JOIN directory_contacts dc ON dc.id=ap.contact_id WHERE ap.animal_id=:animal ORDER BY date(ap.event_date) DESC,ap.id DESC LIMIT 1',
        );
        foreach ($animalIds as $animalId) {
            $death = $db->prepare('SELECT 1 FROM animal_deaths WHERE animal_id=?');
            $death->execute([$animalId]);
            if ($death->fetchColumn()) {
                continue;
            }
            $latest->execute([':animal' => $animalId]);
            $event = $latest->fetch(PDO::FETCH_ASSOC);
            if (!$event) {
                continue;
            }
            $address = implode(
                ' ',
                array_filter([$event['address'] ?? null, $event['postal_code'] ?? null, $event['city'] ?? null]),
            );
            if ($event['event_type'] === 'adoption') {
                $db->prepare(
                    "UPDATE animals SET status='adopte',is_archived=1,archived_at=:archived,current_address=:address,refuge_room=NULL,care_box_key=NULL,quarantine_until=NULL,updated_at=datetime('now') WHERE id=:id",
                )->execute([
                    ':archived' => $event['event_date'] . ' 12:00:00',
                    ':address' =>
                        $address !== ''
                            ? $address
                            : 'Adopté par ' . ($event['contact_name'] ?: 'un adoptant non renseigné'),
                    ':id' => $animalId,
                ]);
            } elseif ($event['event_type'] === 'foster') {
                $db->prepare(
                    "UPDATE animals SET status=CASE WHEN lower(COALESCE(reason,'')) LIKE '%permanente%' THEN 'fa_permanente' ELSE 'fa' END,is_archived=0,archived_at=NULL,current_address=:address,refuge_room=NULL,care_box_key=NULL,updated_at=datetime('now') WHERE id=:id",
                )->execute([
                    ':address' =>
                        $address !== '' ? $address : 'Chez ' . ($event['contact_name'] ?: 'une famille d’accueil'),
                    ':id' => $animalId,
                ]);
            } elseif (in_array($event['event_type'], ['return', 'cancellation'], true)) {
                $db->prepare(
                    "UPDATE animals SET status='refuge',is_archived=0,archived_at=NULL,current_address=NULL,updated_at=datetime('now') WHERE id=?",
                )->execute([$animalId]);
            } elseif ($event['event_type'] === 'reservation') {
                $db->prepare(
                    "UPDATE animals SET status='reserve',is_archived=0,archived_at=NULL,updated_at=datetime('now') WHERE id=?",
                )->execute([$animalId]);
            }
        }
    }
}
