<?php

declare(strict_types=1);

final class DeathController
{
    public const CAUSES = [
        'disease' => 'Maladie',
        'accident' => 'Accident',
        'old_age' => 'Vieillesse',
        'congenital' => 'Malformation ou affection congénitale',
        'surgery_complication' => 'Complication médicale ou chirurgicale',
        'poisoning' => 'Empoisonnement',
        'unknown' => 'Cause inconnue',
        'other' => 'Autre cause',
    ];
    public const PLACES = [
        'refuge' => 'Au refuge',
        'fa' => 'En famille d’accueil',
        'veterinaire' => 'Chez le vétérinaire',
        'exterieur' => 'À l’extérieur',
        'inconnu' => 'Lieu inconnu',
        'autre' => 'Autre lieu',
    ];
    public const DISPOSITIONS = [
        'pending' => 'À organiser',
        'collective_cremation' => 'Crémation collective',
        'individual_cremation' => 'Crémation individuelle',
        'returned_family' => 'Corps ou cendres remis à la famille',
        'burial' => 'Inhumation',
        'other' => 'Autre prise en charge',
    ];

    public static function causeLabel(string $key): string
    {
        return t('death.cause.' . $key, [], self::CAUSES[$key] ?? $key);
    }
    public static function placeLabel(string $key): string
    {
        return t('death.place.' . $key, [], self::PLACES[$key] ?? $key);
    }
    public static function dispositionLabel(string $key): string
    {
        return t('death.disposition.' . $key, [], self::DISPOSITIONS[$key] ?? $key);
    }

    public static function form(): void
    {
        $db = DB::pdo();
        $id = (int) ($_GET['id'] ?? 0);
        $animal = self::animal($db, $id);
        $stmt = $db->prepare(
            'SELECT ad.*,vet.name veterinarian_name,crem.name crematorium_name FROM animal_deaths ad LEFT JOIN directory_contacts vet ON vet.id=ad.veterinarian_contact_id LEFT JOIN directory_contacts crem ON crem.id=ad.crematorium_contact_id WHERE ad.animal_id=:id',
        );
        $stmt->execute([':id' => $id]);
        $death = $stmt->fetch(PDO::FETCH_ASSOC) ?: [
            'animal_id' => $id,
            'deceased_date' => date('Y-m-d'),
            'cause_code' => 'disease',
            'cause_details' => '',
            'occurred_in_care' => !in_array($animal['status'], ['adopte', 'adopté'], true) ? 1 : 0,
            'place_type' => 'refuge',
            'place_details' => '',
            'euthanized' => 0,
            'veterinarian_contact_id' => '',
            'crematorium_contact_id' => '',
            'body_disposition' => 'pending',
            'cremation_date' => '',
            'notes' => '',
        ];
        $veterinarians = self::contacts($db, 'veterinaire');
        $crematoriums = self::contacts($db, 'crematorium');
        render('animal_death_form.php', [
            'title' => t('death.title', ['name' => $animal['name']]),
            'animal' => $animal,
            'death' => $death,
            'causes' => self::CAUSES,
            'places' => self::PLACES,
            'dispositions' => self::DISPOSITIONS,
            'veterinarians' => $veterinarians,
            'crematoriums' => $crematoriums,
        ]);
    }

    public static function save(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            return;
        }
        $db = DB::pdo();
        $id = (int) ($_POST['animal_id'] ?? 0);
        $animal = self::animal($db, $id);
        $date = trim((string) ($_POST['deceased_date'] ?? ''));
        $cause = (string) ($_POST['cause_code'] ?? '');
        $place = (string) ($_POST['place_type'] ?? '');
        $disposition = (string) ($_POST['body_disposition'] ?? 'pending');
        if (
            !self::validDate($date) ||
            $date > date('Y-m-d') ||
            !isset(self::CAUSES[$cause]) ||
            !isset(self::PLACES[$place]) ||
            !isset(self::DISPOSITIONS[$disposition])
        ) {
            http_response_code(400);
            echo h(t('death.invalid'));
            return;
        }
        if ($cause === 'other' && trim((string) ($_POST['cause_details'] ?? '')) === '') {
            http_response_code(400);
            echo h(t('death.cause_required'));
            return;
        }
        $vet = self::validContact($db, (int) ($_POST['veterinarian_contact_id'] ?? 0), 'veterinaire');
        $crem = self::validContact($db, (int) ($_POST['crematorium_contact_id'] ?? 0), 'crematorium');
        $cremationDate = trim((string) ($_POST['cremation_date'] ?? ''));
        if ($cremationDate !== '' && (!self::validDate($cremationDate) || $cremationDate < $date)) {
            http_response_code(400);
            echo h(t('death.cremation_after'));
            return;
        }
        $check = $db->prepare('SELECT 1 FROM animal_deaths WHERE animal_id=:id');
        $check->execute([':id' => $id]);
        $isUpdate = (bool) $check->fetchColumn();
        $beforeLocation = LocationHistoryService::snapshot($animal);
        $params = [
            ':animal' => $id,
            ':date' => $date,
            ':cause' => $cause,
            ':details' => trim((string) ($_POST['cause_details'] ?? '')) ?: null,
            ':care' => isset($_POST['occurred_in_care']) ? 1 : 0,
            ':place' => $place,
            ':place_details' => trim((string) ($_POST['place_details'] ?? '')) ?: null,
            ':euthanized' => isset($_POST['euthanized']) ? 1 : 0,
            ':vet' => $vet,
            ':crem' => $crem,
            ':disposition' => $disposition,
            ':cremation_date' => $cremationDate ?: null,
            ':notes' => trim((string) ($_POST['notes'] ?? '')) ?: null,
            ':user' => Auth::id(),
        ];
        $db->beginTransaction();
        try {
            $db->prepare(
                "INSERT INTO animal_deaths(animal_id,deceased_date,cause_code,cause_details,occurred_in_care,place_type,place_details,euthanized,veterinarian_contact_id,crematorium_contact_id,body_disposition,cremation_date,notes,created_by,updated_by) VALUES(:animal,:date,:cause,:details,:care,:place,:place_details,:euthanized,:vet,:crem,:disposition,:cremation_date,:notes,:user,:user) ON CONFLICT(animal_id) DO UPDATE SET deceased_date=excluded.deceased_date,cause_code=excluded.cause_code,cause_details=excluded.cause_details,occurred_in_care=excluded.occurred_in_care,place_type=excluded.place_type,place_details=excluded.place_details,euthanized=excluded.euthanized,veterinarian_contact_id=excluded.veterinarian_contact_id,crematorium_contact_id=excluded.crematorium_contact_id,body_disposition=excluded.body_disposition,cremation_date=excluded.cremation_date,notes=excluded.notes,updated_by=excluded.updated_by,updated_at=datetime('now')",
            )->execute($params);
            $db->prepare(
                "UPDATE animals SET status='decede',refuge_room=NULL,care_box_key=NULL,updated_at=datetime('now') WHERE id=:id",
            )->execute([':id' => $id]);
            LocationHistoryService::record(
                $db,
                $id,
                $beforeLocation,
                [
                    'status' => 'decede',
                    'refuge_room' => null,
                    'care_box_key' => null,
                    'current_address' => $animal['current_address'] ?? null,
                ],
                'death',
                'Décès enregistré',
                $date . ' 12:00:00',
            );
            $db->prepare(
                'UPDATE treatments SET ongoing=0,end_date=COALESCE(end_date,:date) WHERE animal_id=:id AND ongoing=1',
            )->execute([':date' => $date, ':id' => $id]);
            $details =
                'Date : ' .
                self::frDate($date) .
                '\nCause : ' .
                self::CAUSES[$cause] .
                '\nContexte : ' .
                ($params[':care'] ? 'Sous la responsabilité du refuge' : 'Après la prise en charge du refuge');
            if ($params[':euthanized']) {
                $details .= '\nEuthanasie : oui';
            }
            $db->prepare(
                "INSERT INTO animal_history(animal_id,type,label,details,user_id) VALUES(:id,'death',:label,:details,:user)",
            )->execute([
                ':id' => $id,
                ':label' => $isUpdate ? 'Informations de décès modifiées' : 'Décès enregistré',
                ':details' => $details,
                ':user' => Auth::id(),
            ]);
            self::syncMovement(
                $db,
                $id,
                $date,
                $cause,
                $params[':details'],
                $place,
                $params[':place_details'],
                $params[':euthanized'] === 1,
                $vet,
                $crem,
                $disposition,
            );
            $db->commit();
            header('Location: /animal?id=' . $id);
            exit();
        } catch (Throwable $e) {
            if ($db->inTransaction()) {
                $db->rollBack();
            }
            throw $e;
        }
    }

    private static function animal(PDO $db, int $id): array
    {
        $s = $db->prepare('SELECT * FROM animals WHERE id=:id AND deleted_at IS NULL');
        $s->execute([':id' => $id]);
        $a = $s->fetch(PDO::FETCH_ASSOC);
        if (!$a) {
            http_response_code(404);
            echo h(t('error.animal_not_found'));
            exit();
        }
        return $a;
    }
    private static function contacts(PDO $db, string $role): array
    {
        $s = $db->prepare(
            'SELECT dc.id,dc.name,dc.city FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id WHERE r.role=:role AND dc.deleted_at IS NULL ORDER BY dc.name COLLATE NOCASE',
        );
        $s->execute([':role' => $role]);
        return $s->fetchAll(PDO::FETCH_ASSOC);
    }
    private static function validContact(PDO $db, int $id, string $role): ?int
    {
        if (!$id) {
            return null;
        }
        $s = $db->prepare(
            'SELECT 1 FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id WHERE dc.id=:id AND r.role=:role AND dc.deleted_at IS NULL',
        );
        $s->execute([':id' => $id, ':role' => $role]);
        return $s->fetchColumn() ? $id : null;
    }
    private static function syncMovement(
        PDO $db,
        int $animalId,
        string $date,
        string $cause,
        ?string $causeDetails,
        string $place,
        ?string $placeDetails,
        bool $euthanized,
        ?int $vetId,
        ?int $cremId,
        string $disposition,
    ): void {
        $names = [];
        if ($vetId || $cremId) {
            $s = $db->prepare('SELECT id,name FROM directory_contacts WHERE id IN (:vet,:crem)');
            $s->execute([':vet' => $vetId ?? 0, ':crem' => $cremId ?? 0]);
            foreach ($s->fetchAll(PDO::FETCH_ASSOC) as $row) {
                $names[(int) $row['id']] = $row['name'];
            }
        }
        $noteParts = ['Cause : ' . self::CAUSES[$cause] . ($causeDetails ? ' — ' . $causeDetails : '')];
        if ($euthanized) {
            $noteParts[] = 'Euthanasie : oui';
        }
        if ($cremId && isset($names[$cremId])) {
            $noteParts[] = 'Crématorium : ' . $names[$cremId];
        }
        $noteParts[] = 'Prise en charge : ' . self::DISPOSITIONS[$disposition];
        $s = $db->prepare(
            "SELECT id FROM animal_movements WHERE animal_id=:animal AND kind='exit' AND lower(lieu) IN ('décès','deces','décès de l''animal','deces de l''animal') ORDER BY id DESC LIMIT 1",
        );
        $s->execute([':animal' => $animalId]);
        $movementId = (int) $s->fetchColumn();
        $values = [
            ':animal' => $animalId,
            ':place' => self::PLACES[$place] . ($placeDetails ? ' — ' . $placeDetails : ''),
            ':contact' => $vetId && isset($names[$vetId]) ? $names[$vetId] : null,
            ':note' => implode("\n", $noteParts),
            ':at' => $date . ' 12:00:00',
        ];
        if ($movementId) {
            $values[':movement'] = $movementId;
            $db->prepare(
                "UPDATE animal_movements SET place=:place,lieu='Décès de l''animal',contact_name=:contact,note=:note,created_at=:at WHERE id=:movement AND animal_id=:animal",
            )->execute($values);
        } else {
            $db->prepare(
                "INSERT INTO animal_movements(animal_id,kind,place,lieu,contact_name,note,created_at) VALUES(:animal,'exit',:place,'Décès de l''animal',:contact,:note,:at)",
            )->execute($values);
        }
    }
    private static function validDate(string $date): bool
    {
        $d = DateTimeImmutable::createFromFormat('!Y-m-d', $date);
        $errors = DateTimeImmutable::getLastErrors();
        return $d !== false &&
            ($errors === false || ((int) $errors['warning_count'] === 0 && (int) $errors['error_count'] === 0)) &&
            $d->format('Y-m-d') === $date;
    }
    private static function frDate(string $date): string
    {
        $d = DateTimeImmutable::createFromFormat('Y-m-d', $date);
        return $d ? $d->format('d/m/Y') : $date;
    }
}
