<?php

declare(strict_types=1);

final class DemoDataService
{
    private const TABLES = [
        'agenda_volunteers',
        'agenda_items',
        'prescription_treatments',
        'lab_results',
        'medical_prescriptions',
        'lab_reports',
        'treatment_administrations',
        'care_round_observations',
        'care_rounds',
        'medical_photos',
        'animal_photos',
        'animal_expenses',
        'animal_surgeries',
        'animal_health_conditions',
        'dewormings',
        'vaccinations',
        'treatments',
        'medical_notes',
        'measurements',
        'animal_placements',
        'adoptions',
        'animal_movements',
        'animal_deaths',
        'animal_location_history',
        'animal_history',
        'litter_kittens',
        'litters',
        'bonded_group_members',
        'bonded_groups',
        'icad_cache',
        'asm3_import_links',
        'asm3_import_runs',
        'asm3_import_jobs',
        'directory_contact_roles',
        'animals',
        'directory_contacts',
        'grant_documents',
        'grant_applications',
        'grant_years',
        'care_room_layouts',
        'clinics',
        'geocode_cache',
        'audit_log',
    ];

    public static function reset(PDO $db): array
    {
        $before = [
            'animals' => (int) $db->query('SELECT COUNT(*) FROM animals')->fetchColumn(),
            'contacts' => (int) $db->query('SELECT COUNT(*) FROM directory_contacts')->fetchColumn(),
        ];
        $sequence = $db->prepare('DELETE FROM sqlite_sequence WHERE name=?');
        $exists = $db->prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?");
        foreach (self::TABLES as $table) {
            $exists->execute([$table]);
            if (!$exists->fetchColumn()) {
                continue;
            }
            $db->exec('DELETE FROM ' . $table);
            $sequence->execute([$table]);
        }
        return $before;
    }

    public static function seed(PDO $db, ?int $userId): array
    {
        $contacts = [
            'fa' => [
                'person',
                'Alice Martin',
                '06 00 00 00 01',
                'alice.demo@example.org',
                '12 rue des Lilas',
                '29260',
                'Lesneven',
            ],
            'adopter' => [
                'person',
                'Marc Durand',
                '06 00 00 00 02',
                'marc.demo@example.org',
                '8 rue du Port',
                '29200',
                'Brest',
            ],
            'depositor' => [
                'person',
                'Sophie Le Gall',
                '06 00 00 00 03',
                'sophie.demo@example.org',
                '4 place du Marché',
                '29000',
                'Ville fictive',
            ],
            'vet' => [
                'person',
                'Dr Camille Morel',
                '02 00 00 00 04',
                'veterinaire.demo@example.org',
                '2 avenue des Chats',
                '29260',
                'Lesneven',
            ],
            'clinic' => [
                'organization',
                'Clinique vétérinaire Démo',
                '02 00 00 00 05',
                'clinique.demo@example.org',
                '2 avenue des Chats',
                '29260',
                'Lesneven',
            ],
            'crematorium' => [
                'organization',
                'Crématorium animalier Démo',
                '02 00 00 00 06',
                'crematorium.demo@example.org',
                '1 route du Souvenir',
                '29400',
                'Landivisiau',
            ],
        ];
        $contactIds = [];
        $insert = $db->prepare(
            'INSERT INTO directory_contacts(kind,name,phone,email,address,postal_code,city,notes) VALUES(?,?,?,?,?,?,?,?)',
        );
        $role = $db->prepare('INSERT INTO directory_contact_roles(contact_id,role) VALUES(?,?)');
        foreach ($contacts as $key => $c) {
            $insert->execute([...$c, 'Donnée fictive de démonstration']);
            $contactIds[$key] = (int) $db->lastInsertId();
            $roleName = match ($key) {
                'adopter' => 'adoptant',
                'vet' => 'veterinaire',
                'clinic' => 'cabinet',
                'crematorium' => 'crematorium',
                'depositor' => 'deposant',
                default => 'fa',
            };
            $role->execute([$contactIds[$key], $roleName]);
        }

        $animals = [
            [
                'DEMO-001',
                'Moka',
                'F',
                '2019-05-14',
                'refuge',
                'silver',
                'Bicolore Noir / Blanc',
                'yes',
                'yes',
                'yes',
                'yes',
                'yes',
                'available',
            ],
            [
                'DEMO-002',
                'Pixel',
                'M',
                '2021-09-03',
                'refuge',
                'twix',
                'Roux',
                'yes',
                'yes',
                'yes',
                'yes',
                'no',
                'available',
            ],
            [
                'DEMO-003',
                'Twix',
                'M',
                '2019-04-15',
                'quarantaine',
                null,
                'Roux',
                'yes',
                'yes',
                'yes',
                'yes',
                'yes',
                'not_available',
            ],
            [
                'DEMO-004',
                'Nox',
                'M',
                '2023-02-18',
                'soin',
                null,
                'Noir',
                'no',
                'yes',
                'unknown',
                'yes',
                'no',
                'not_available',
            ],
            [
                'DEMO-005',
                'Caramel',
                'F',
                '2020-07-21',
                'fa',
                null,
                'Écaille de tortue',
                'yes',
                'yes',
                'yes',
                'yes',
                'yes',
                'available',
            ],
            [
                'DEMO-006',
                'Plume',
                'F',
                '2022-04-08',
                'refuge',
                'silver',
                'Tricolore / Calico',
                'yes',
                'yes',
                'yes',
                'yes',
                'yes',
                'available',
            ],
            [
                'DEMO-007',
                'Plume-Bébé-1',
                'F',
                date('Y-m-d', strtotime('-4 months')),
                'refuge',
                'silver',
                'Bicolore Gris / Blanc',
                'unknown',
                'yes',
                'unknown',
                'unknown',
                'unknown',
                'not_available',
            ],
            [
                'DEMO-008',
                'Plume-Bébé-2',
                'M',
                date('Y-m-d', strtotime('-4 months')),
                'refuge',
                'silver',
                'Gris',
                'unknown',
                'yes',
                'unknown',
                'unknown',
                'unknown',
                'not_available',
            ],
            [
                'DEMO-009',
                'Plume-Bébé-3',
                'U',
                date('Y-m-d', strtotime('-4 months')),
                'refuge',
                'silver',
                'Tigré / Tabby',
                'unknown',
                'yes',
                'unknown',
                'unknown',
                'unknown',
                'not_available',
            ],
            [
                'DEMO-010',
                'Lune',
                'F',
                '2021-01-12',
                'refuge',
                'twix',
                'Blanc',
                'yes',
                'no',
                'yes',
                'yes',
                'yes',
                'available',
            ],
            [
                'DEMO-011',
                'Soleil',
                'M',
                '2021-01-12',
                'refuge',
                'twix',
                'Roux',
                'yes',
                'no',
                'yes',
                'yes',
                'yes',
                'available',
            ],
            [
                'DEMO-012',
                'Étoile',
                'F',
                '2012-06-01',
                'decede',
                null,
                'Noir',
                'yes',
                'unknown',
                'unknown',
                'unknown',
                'unknown',
                'not_available',
            ],
        ];
        $animalIds = [];
        $animal = $db->prepare(
            "INSERT INTO animals(internal_code,name,species,sex,birth_date,status,refuge_room,care_box_key,color,sterilized,sterilization_status,compatibility_cats,compatibility_dogs,compatibility_children,house_trained,adoption_availability,intake_date,intake_type,intake_reason,rescue_location_name,current_address,chip_id,identification_type,identification_registration_status,archived_at,notes) VALUES(:code,:name,'chat',:sex,:birth,:status,:room,:box,:color,:sterilized,:sterilization,:cats,:dogs,:children,:clean,:availability,date('now','-'||:days||' days'),'found','stray_found','Commune fictive','5 rue de la Démonstration, 29260 Lesneven',:chip,:identification,:registration,:archived,'Donnée fictive de démonstration')",
        );
        foreach ($animals as $i => $a) {
            [
                $code,
                $name,
                $sex,
                $birth,
                $status,
                $room,
                $color,
                $sterilization,
                $cats,
                $dogs,
                $children,
                $clean,
                $availability,
            ] = $a;
            $sterilized = $sterilization === 'yes' ? 1 : 0;
            $chip = $i < 6 ? '250269900' . str_pad((string) ($i + 1), 6, '0', STR_PAD_LEFT) : null;
            $animal->execute([
                ':code' => $code,
                ':name' => $name,
                ':sex' => $sex,
                ':birth' => $birth,
                ':status' => $status,
                ':room' => $room,
                ':box' => $status === 'soin' ? 'care-top-1' : null,
                ':color' => $color,
                ':sterilized' => $sterilized,
                ':sterilization' => $sterilization,
                ':cats' => $cats,
                ':dogs' => $dogs,
                ':children' => $children,
                ':clean' => $clean,
                ':availability' => $availability,
                ':days' => 90 + $i * 12,
                ':chip' => $chip,
                ':identification' => $chip ? 'microchip' : 'unknown',
                ':registration' => $chip ? 'registered' : 'unknown',
                ':archived' => $status === 'decede' ? date('Y-m-d H:i:s') : null,
            ]);
            $animalIds[$name] = (int) $db->lastInsertId();
        }

        $db->prepare('INSERT INTO litters(mother_id,birth_date,notes) VALUES(?,?,?)')->execute([
            $animalIds['Plume'],
            $animals[6][3],
            'Portée fictive de démonstration',
        ]);
        $litter = (int) $db->lastInsertId();
        $link = $db->prepare('INSERT INTO litter_kittens(litter_id,animal_id,position) VALUES(?,?,?)');
        foreach (['Plume-Bébé-1', 'Plume-Bébé-2', 'Plume-Bébé-3'] as $i => $name) {
            $link->execute([$litter, $animalIds[$name], $i + 1]);
        }
        $db->prepare('INSERT INTO bonded_groups(name,notes) VALUES(?,?)')->execute([
            'Duo Lune & Soleil',
            'Chats fictifs à adopter ensemble',
        ]);
        $group = (int) $db->lastInsertId();
        $member = $db->prepare('INSERT INTO bonded_group_members(group_id,animal_id,position) VALUES(?,?,?)');
        $member->execute([$group, $animalIds['Lune'], 1]);
        $member->execute([$group, $animalIds['Soleil'], 2]);

        $twix = $animalIds['Twix'];
        $db->prepare(
            "UPDATE animals SET breed='Européen',birth_is_estimated=0,fiv=0,felv=0,quarantine_until=date('now','+10 days'),intake_circumstances='Trouvé seul, bilan sanitaire programmé à l’arrivée.',depositor_contact_id=?,rescue_location_name='Commune fictive',current_address='Quarantaine — box supérieur gauche',identification_date=date('now','-2 years'),notes='Fiche vitrine entièrement fictive : elle illustre un dossier animal complet.' WHERE id=?",
        )->execute([$contactIds['depositor'], $twix]);

        $db->prepare(
            "INSERT OR IGNORE INTO ref_medications(name,molecule,form,notes) VALUES('Doxybactin','doxycycline','comprimé','Référence de démonstration')",
        )->execute();
        $med = (int) $db->query("SELECT id FROM ref_medications WHERE name='Doxybactin'")->fetchColumn();
        $db->prepare(
            "INSERT INTO treatments(animal_id,medication_id,route,dose_text,start_date,end_date,ongoing,notes,created_by,give_morning,give_evening) VALUES(?,?,'PO','1/2 comprimé matin et soir',date('now','-2 days'),date('now','+5 days'),1,'Traitement fictif de démonstration',?,1,1)",
        )->execute([$twix, $med, $userId]);
        $db->prepare(
            "INSERT INTO medical_notes(animal_id,noted_at,kind,reason,symptoms,exam,diagnosis,plan,weight_kg,temperature_c,created_by,vet_contact_id,clinic_contact_id) VALUES(?,datetime('now','-1 day'),'consult','Visite sanitaire d’entrée','Éternuements occasionnels','État général satisfaisant, auscultation sans anomalie','Coryza léger','Surveillance, traitement court et contrôle avant sortie de quarantaine',4.8,38.4,?,?,?)",
        )->execute([$twix, $userId, $contactIds['vet'], $contactIds['clinic']]);
        $weight = $db->prepare(
            "INSERT INTO measurements(animal_id,type,value,unit,measured_at,notes,created_by) VALUES(?,'weight',?,'kg',datetime('now',?),'Mesure fictive',?)",
        );
        foreach ([[4.6, '-30 days'], [4.7, '-14 days'], [4.8, '-1 day']] as $w) {
            $weight->execute([$twix, $w[0], $w[1], $userId]);
        }
        $db->prepare(
            "INSERT OR IGNORE INTO ref_vaccines(name,notes) VALUES('TCL','Vaccin de démonstration')",
        )->execute();
        $vaccine = (int) $db->query("SELECT id FROM ref_vaccines WHERE name='TCL'")->fetchColumn();
        $db->prepare(
            "INSERT INTO vaccinations(animal_id,vaccine_id,done_date,due_date,lot,manufacturer,notes,created_by,vet_contact_id,clinic_contact_id) VALUES(?,?,date('now','-11 months'),date('now','+1 month'),'LOT-DEMO-1','Laboratoire Démo','Vaccination fictive à jour',?,?,?)",
        )->execute([$twix, $vaccine, $userId, $contactIds['vet'], $contactIds['clinic']]);
        $dewormer = (int) $db->query('SELECT id FROM ref_dewormers ORDER BY id LIMIT 1')->fetchColumn();
        $db->prepare(
            "INSERT INTO dewormings(animal_id,dewormer_id,administered_on,dose_text,weight_kg,next_due_date,notes,created_by) VALUES(?,?,date('now','-10 days'),'1 pipette',4.7,date('now','+80 days'),'Administration fictive',?)",
        )->execute([$twix, $dewormer, $userId]);
        $db->prepare(
            "INSERT INTO animal_health_conditions(animal_id,name,status,diagnosed_at,resolved_at,notes) VALUES(?,'Gingivite légère','resolved',date('now','-6 months'),date('now','-5 months'),'Antécédent fictif résolu')",
        )->execute([$twix]);
        $db->prepare(
            "INSERT INTO lab_reports(animal_id,sampled_on,report_type,laboratory_name,veterinarian_contact_id,clinic_contact_id,notes,created_by) VALUES(?,date('now','-1 day'),'Bilan complet','Laboratoire Démo',?,?,'Prise de sang fictive — valeurs dans les intervalles usuels',?)",
        )->execute([$twix, $contactIds['vet'], $contactIds['clinic'], $userId]);
        $report = (int) $db->lastInsertId();
        $result = $db->prepare(
            'INSERT INTO lab_results(report_id,parameter_name,value,unit,reference_min,reference_max,notes,position) VALUES(?,?,?,?,?,?,?,?)',
        );
        foreach (
            [
                ['Leucocytes', 8.4, '10⁹/L', 3.5, 20.7, 10],
                ['Érythrocytes', 9.6, '10¹²/L', 7.7, 12.8, 20],
                ['Hémoglobine', 13.8, 'g/dL', 10, 17, 30],
                ['Hématocrite', 42.1, '%', 33.7, 55.4, 40],
                ['Plaquettes', 312, '10⁹/L', 125, 618, 50],
                ['Créatinine', 12, 'mg/L', 3, 21, 60],
                ['Urée', 0.42, 'g/L', 0.214, 0.642, 70],
            ]
            as $r
        ) {
            $result->execute([$report, $r[0], $r[1], $r[2], $r[3], $r[4], 'Résultat fictif', $r[5]]);
        }
        $db->prepare(
            "INSERT INTO animal_photos(animal_id,filename,original_name,mime,size_bytes,is_primary,is_public,caption) VALUES(?,'twix.webp','twix.jpg','image/webp',0,1,1,'Twix — photographie de démonstration')",
        )->execute([$twix]);
        $db->prepare(
            "INSERT INTO agenda_items(item_type,title,description,starts_at,ends_at,location,status,priority,animal_id,contact_id,reminder_minutes,created_by) VALUES('veterinary','Contrôle de sortie de quarantaine','Rendez-vous fictif : contrôle clinique et validation de la sortie.',datetime('now','+7 days','09:00'),datetime('now','+7 days','09:30'),'Clinique vétérinaire Démo','planned','high',?,?,60,?)",
        )->execute([$twix, $contactIds['clinic'], $userId]);
        $db->prepare(
            "INSERT INTO animal_placements(animal_id,event_type,event_date,contact_id,reason,notes,created_by) VALUES(?,'foster',date('now','-30 days'),?,'Placement de démonstration','Donnée fictive',?)",
        )->execute([$animalIds['Caramel'], $contactIds['fa'], $userId]);
        $db->prepare(
            "INSERT INTO animal_deaths(animal_id,deceased_date,cause_code,cause_details,occurred_in_care,place_type,euthanized,veterinarian_contact_id,crematorium_contact_id,body_disposition,created_by) VALUES(?,date('now','-60 days'),'old_age','Décès fictif',1,'veterinaire',1,?,?,'collective_cremation',?)",
        )->execute([$animalIds['Étoile'], $contactIds['vet'], $contactIds['crematorium'], $userId]);
        $history = $db->prepare(
            "INSERT INTO animal_history(animal_id,action,field,new_value,user_id,created_at) VALUES(?,'created','demo','Donnée entièrement fictive',?,datetime('now'))",
        );
        foreach ($animalIds as $id) {
            $history->execute([$id, $userId]);
        }
        return [
            'animals' => count($animalIds),
            'contacts' => count($contactIds),
            'litters' => 1,
            'bonded_groups' => 1,
            'treatments' => 1,
            'vaccinations' => 1,
            'lab_reports' => 1,
            'agenda_items' => 1,
            'photos' => 1,
        ];
    }

    public static function clearFiles(): void
    {
        foreach (
            [
                dirname(__DIR__, 2) . '/public/media/animals',
                dirname(__DIR__, 2) . '/data/grants',
                dirname(__DIR__, 2) . '/data/medical-documents',
            ]
            as $root
        ) {
            if (!is_dir($root)) {
                continue;
            }
            $it = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
                RecursiveIteratorIterator::CHILD_FIRST,
            );
            foreach ($it as $file) {
                $path = $file->getPathname();
                if ($file->isLink() || $file->isFile()) {
                    @unlink($path);
                } elseif ($file->isDir()) {
                    @rmdir($path);
                }
            }
        }
    }

    public static function installFiles(PDO $db): void
    {
        $animalId = (int) $db->query("SELECT id FROM animals WHERE internal_code='DEMO-003' LIMIT 1")->fetchColumn();
        if ($animalId <= 0) {
            return;
        }
        $source = dirname(__DIR__, 2) . '/resources/demo/twix.webp';
        $directory = dirname(__DIR__, 2) . '/public/media/animals/' . $animalId;
        $target = $directory . '/twix.webp';
        if (!is_file($source)) {
            throw new RuntimeException(t('service.image.invalid_upload'));
        }
        if (!is_dir($directory) && !mkdir($directory, 0755, true) && !is_dir($directory)) {
            throw new RuntimeException(t('service.image.directory_failed'));
        }
        if (!copy($source, $target)) {
            throw new RuntimeException(t('service.image.save_failed'));
        }
        @chmod($target, 0644);
        $db->prepare("UPDATE animal_photos SET size_bytes=? WHERE animal_id=? AND filename='twix.webp'")->execute([
            filesize($target),
            $animalId,
        ]);
    }
}
