<?php

declare(strict_types=1);

trait AnimalMedicalActions
{
    public static function updateMedicalStatus(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            return;
        }

        $animalId = (int) ($_POST['animal_id'] ?? 0);
        if ($animalId <= 0) {
            http_response_code(400);
            return;
        }

        $allowedTestStatuses = ['unknown', 'neg', 'pos', 'doubtful'];
        $fivStatus = (string) ($_POST['fiv_status'] ?? 'unknown');
        $felvStatus = (string) ($_POST['felv_status'] ?? 'unknown');
        if (!in_array($fivStatus, $allowedTestStatuses, true)) {
            $fivStatus = 'unknown';
        }
        if (!in_array($felvStatus, $allowedTestStatuses, true)) {
            $felvStatus = 'unknown';
        }

        $testDate = trim((string) ($_POST['test_date'] ?? date('Y-m-d')));
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $testDate)) {
            $testDate = date('Y-m-d');
        }
        $testNotes = trim((string) ($_POST['test_notes'] ?? ''));
        $conditions = is_array($_POST['conditions'] ?? null) ? $_POST['conditions'] : [];

        $db = DB::pdo();
        $animalStmt = $db->prepare('SELECT fiv, felv, fiv_status, felv_status FROM animals WHERE id = :id');
        $animalStmt->execute([':id' => $animalId]);
        $old = $animalStmt->fetch(PDO::FETCH_ASSOC);
        if (!$old) {
            http_response_code(404);
            return;
        }

        $oldFivStatus = (string) ($old['fiv_status'] ?: ((int) $old['fiv'] === 1 ? 'pos' : 'neg'));
        $oldFelvStatus = (string) ($old['felv_status'] ?: ((int) $old['felv'] === 1 ? 'pos' : 'neg'));

        $db->beginTransaction();
        try {
            $db->prepare(
                "
            UPDATE animals
            SET fiv_status = :fiv_status,
                felv_status = :felv_status,
                fiv = :fiv,
                felv = :felv,
                updated_at = datetime('now')
            WHERE id = :id
            ",
            )->execute([
                ':fiv_status' => $fivStatus,
                ':felv_status' => $felvStatus,
                ':fiv' => $fivStatus === 'pos' ? 1 : 0,
                ':felv' => $felvStatus === 'pos' ? 1 : 0,
                ':id' => $animalId,
            ]);

            $noteStmt = $db->prepare("
            INSERT INTO medical_notes
              (animal_id, noted_at, kind, reason, diagnosis, plan, created_at)
            VALUES
              (:animal_id, :noted_at, 'diagnostic', :reason, :diagnosis, :plan, datetime('now'))
            ");

            foreach (
                [
                    ['label' => 'FIV', 'old' => $oldFivStatus, 'new' => $fivStatus],
                    ['label' => 'FeLV', 'old' => $oldFelvStatus, 'new' => $felvStatus],
                ]
                as $test
            ) {
                if ($test['new'] === 'pos' && $test['old'] !== 'pos') {
                    $diagnosis = $test['label'] . ' positif';
                    $noteStmt->execute([
                        ':animal_id' => $animalId,
                        ':noted_at' => $testDate . ' 12:00:00',
                        ':reason' => 'Résultat de test ' . $test['label'],
                        ':diagnosis' => $diagnosis,
                        ':plan' => $testNotes !== '' ? $testNotes : null,
                    ]);
                    self::logHistory(
                        $animalId,
                        'medical',
                        $diagnosis,
                        'Test positif le ' . $testDate . ($testNotes !== '' ? "\n" . $testNotes : ''),
                    );
                }
            }

            $conditionSelect = $db->prepare(
                'SELECT status FROM animal_health_conditions WHERE id = :id AND animal_id = :animal_id',
            );
            $conditionUpdate = $db->prepare("
            UPDATE animal_health_conditions
            SET name = :name, status = :status, diagnosed_at = :diagnosed_at,
                resolved_at = :resolved_at, notes = :notes, updated_at = datetime('now')
            WHERE id = :id AND animal_id = :animal_id
            ");
            $conditionInsert = $db->prepare('
            INSERT INTO animal_health_conditions
              (animal_id, name, status, diagnosed_at, resolved_at, notes)
            VALUES
              (:animal_id, :name, :status, :diagnosed_at, :resolved_at, :notes)
            ');

            foreach ($conditions as $key => $condition) {
                if (!is_array($condition)) {
                    continue;
                }
                $name = trim((string) ($condition['name'] ?? ''));
                if ($name === '') {
                    continue;
                }
                $status = ($condition['status'] ?? 'active') === 'resolved' ? 'resolved' : 'active';
                $diagnosedAt = trim((string) ($condition['diagnosed_at'] ?? '')) ?: null;
                $notes = trim((string) ($condition['notes'] ?? '')) ?: null;
                $conditionId = ctype_digit((string) $key) ? (int) $key : 0;
                $previousStatus = null;

                if ($conditionId > 0) {
                    $conditionSelect->execute([':id' => $conditionId, ':animal_id' => $animalId]);
                    $previousStatus = $conditionSelect->fetchColumn();
                    if ($previousStatus === false) {
                        continue;
                    }
                    $conditionUpdate->execute([
                        ':name' => $name,
                        ':status' => $status,
                        ':diagnosed_at' => $diagnosedAt,
                        ':resolved_at' => $status === 'resolved' ? date('Y-m-d') : null,
                        ':notes' => $notes,
                        ':id' => $conditionId,
                        ':animal_id' => $animalId,
                    ]);
                } else {
                    $conditionInsert->execute([
                        ':animal_id' => $animalId,
                        ':name' => $name,
                        ':status' => $status,
                        ':diagnosed_at' => $diagnosedAt,
                        ':resolved_at' => $status === 'resolved' ? date('Y-m-d') : null,
                        ':notes' => $notes,
                    ]);
                }

                if ($status === 'active' && $previousStatus !== 'active') {
                    $noteStmt->execute([
                        ':animal_id' => $animalId,
                        ':noted_at' => ($diagnosedAt ?: date('Y-m-d')) . ' 12:00:00',
                        ':reason' => 'Statut médical global',
                        ':diagnosis' => $name,
                        ':plan' => $notes,
                    ]);
                    self::logHistory(
                        $animalId,
                        'medical',
                        'Pathologie / vigilance ajoutée',
                        $name . ($notes ? "\n" . $notes : ''),
                    );
                }
            }

            $changes = [];
            if ($oldFivStatus !== $fivStatus) {
                $changes[] = 'FIV : ' . $oldFivStatus . ' → ' . $fivStatus;
            }
            if ($oldFelvStatus !== $felvStatus) {
                $changes[] = 'FeLV : ' . $oldFelvStatus . ' → ' . $felvStatus;
            }
            if ($changes) {
                self::logHistory($animalId, 'medical', 'Statut médical mis à jour', implode("\n", $changes));
            }

            $db->commit();
        } catch (Throwable $e) {
            $db->rollBack();
            throw $e;
        }

        header('Location: /animal?id=' . $animalId . '#tab-med');
        exit();
    }

    public static function addMedical(): void
    {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            http_response_code(405);
            return;
        }

        $animalId = (int) ($_POST['animal_id'] ?? 0);
        if ($animalId <= 0) {
            http_response_code(400);
            return;
        }

        $kind = trim($_POST['kind'] ?? 'consult');
        $reason = trim($_POST['reason'] ?? '');
        $symptoms = trim($_POST['symptoms'] ?? '');
        $exam = trim($_POST['exam'] ?? '');
        $diagnosis = trim($_POST['diagnosis'] ?? '');
        $plan = trim($_POST['plan'] ?? '');
        $markSterilized = !empty($_POST['mark_sterilized']);
        if ($markSterilized) {
            $kind = 'acte';
            if ($reason === '') {
                $reason = 'Stérilisation';
            }
        }

        $temperature = $_POST['temperature_c'] ?? null;
        $weight = $_POST['weight_kg'] ?? null;

        $clinicId = (int) ($_POST['clinic_id'] ?? 0);
        $clinicId = $clinicId > 0 ? $clinicId : null;

        $db = DB::pdo();
        $clinicContact = self::resolveDirectoryContact(
            $db,
            (int) ($_POST['clinic_contact_id'] ?? 0),
            (string) ($_POST['new_clinic_name'] ?? ''),
            'organization',
            'cabinet',
        );
        $vetContact = self::resolveDirectoryContact(
            $db,
            (int) ($_POST['vet_contact_id'] ?? 0),
            (string) ($_POST['new_vet_name'] ?? ''),
            'person',
            'veterinaire',
        );
        self::normalizeVeterinaryClinic($db, $vetContact, $clinicContact);
        if (!$vetContact || !$clinicContact) {
            http_response_code(400);
            echo h(t('error.vet_clinic_required'));
            return;
        }
        $vetName = $vetContact['name'] ?? null;

        // 1️⃣ Insertion note médicale
        $stmt = $db->prepare('
        INSERT INTO medical_notes
        (animal_id, kind, reason, symptoms, exam, diagnosis, plan, temperature_c, weight_kg, clinic_id, vet_name, clinic_contact_id, vet_contact_id, created_by, created_at)
        VALUES
        (:aid, :kind, :reason, :symptoms, :exam, :diagnosis, :plan, :temp, :weight, :clinic, :vet, :clinic_contact, :vet_contact, :created_by, :created)
        ');

        $stmt->execute([
            ':aid' => $animalId,
            ':kind' => $kind,
            ':reason' => $reason,
            ':symptoms' => $symptoms,
            ':exam' => $exam,
            ':diagnosis' => $diagnosis,
            ':plan' => $plan,
            ':temp' => $temperature !== '' ? (float) $temperature : null,
            ':weight' => $weight !== '' ? (float) $weight : null,
            ':clinic' => $clinicId,
            ':vet' => $vetName,
            ':clinic_contact' => $clinicContact['id'] ?? null,
            ':vet_contact' => $vetContact['id'] ?? null,
            ':created_by' => Auth::id(),
            ':created' => self::now(),
        ]);

        // 👉 RÉCUPÉRATION ID NOTE
        $medicalNoteId = $db->lastInsertId();
        if ((int) ($_POST['tariff_id'] ?? 0) > 0) {
            PricingService::record(
                $db,
                $animalId,
                (int) $_POST['tariff_id'],
                1,
                substr(self::now(), 0, 10),
                'Ajoutée avec la note médicale',
            );
        }

        // ==========================
        // 2️⃣ Upload photos médicales
        // ==========================

        if (!empty($_FILES['medical_photos']['name'][0])) {
            $uploadDir = PrivateMediaService::medicalDir($animalId);

            if (!is_dir($uploadDir)) {
                PrivateMediaService::ensure($uploadDir);
            }

            foreach ($_FILES['medical_photos']['tmp_name'] as $key => $tmpName) {
                if ($_FILES['medical_photos']['error'][$key] !== UPLOAD_ERR_OK) {
                    continue;
                }

                if ($_FILES['medical_photos']['size'][$key] > 20 * 1024 * 1024) {
                    continue;
                }

                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $mime = finfo_file($finfo, $tmpName);

                $allowedTypes = [
                    'image/jpeg' => 'jpg',
                    'image/png' => 'png',
                    'image/webp' => 'webp',
                ];

                if (!isset($allowedTypes[$mime])) {
                    continue;
                }

                try {
                    $stored = ImageService::storeUploaded($tmpName, $uploadDir, 'med', 'medical');
                    $safeName = $stored['filename'];
                    @chmod($stored['path'], 0600);

                    $stmtPhoto = $db->prepare('
                    INSERT INTO medical_photos (animal_id, medical_note_id, filename)
                    VALUES (?, ?, ?)
                    ');

                    $stmtPhoto->execute([$animalId, $medicalNoteId, $safeName]);
                } catch (Throwable) {
                    continue;
                }
            }
        }

        // 3️⃣ Poids → measurements (graph)
        if ($weight !== '' && is_numeric($weight)) {
            $db->prepare(
                "
            INSERT INTO measurements (animal_id, measured_at, type, value, unit, notes, created_by)
            VALUES (:aid, :at, 'weight', :v, 'kg', 'from medical note', :created_by)
            ",
            )->execute([
                ':aid' => $animalId,
                ':at' => self::now(),
                ':v' => (float) $weight,
                ':created_by' => Auth::id(),
            ]);
        }

        // 4️⃣ Historique
        self::logHistory($animalId, 'medical', 'Note médicale ajoutée', $reason ?: null);

        // 5️⃣ Touch animal
        if ($markSterilized) {
            $db->prepare(
                "UPDATE animals SET sterilized=1,sterilization_status='yes',sterilization_date=COALESCE(sterilization_date,date(:date)),sterilization_vet_contact_id=COALESCE(:vet,sterilization_vet_contact_id),sterilization_clinic_contact_id=COALESCE(:clinic,sterilization_clinic_contact_id) WHERE id=:id",
            )->execute([
                ':date' => self::now(),
                ':vet' => $vetContact['id'] ?? null,
                ':clinic' => $clinicContact['id'] ?? null,
                ':id' => $animalId,
            ]);
        }
        $db->prepare(
            '
        UPDATE animals SET updated_at = :u WHERE id = :id
        ',
        )->execute([
            ':u' => self::now(),
            ':id' => $animalId,
        ]);

        self::redirectToAnimal($animalId);
    }

    public static function addTreatment(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            echo h(t('error.method_not_allowed'));
            return;
        }

        $id = (int) ($_POST['animal_id'] ?? 0);
        $medId = (int) ($_POST['medication_id'] ?? 0);
        $createMedication = !empty($_POST['create_medication']);
        if ($id <= 0 || (!$createMedication && $medId <= 0)) {
            http_response_code(400);
            echo h(t('error.bad_request'));
            return;
        }

        $route = trim((string) ($_POST['route'] ?? ''));
        $dose = trim((string) ($_POST['dose_text'] ?? ''));
        $start = trim((string) ($_POST['start_date'] ?? date('Y-m-d')));
        $end = trim((string) ($_POST['end_date'] ?? ''));
        $notes = trim((string) ($_POST['notes'] ?? ''));
        $giveMorning = !empty($_POST['give_morning']) ? 1 : 0;
        $giveEvening = !empty($_POST['give_evening']) ? 1 : 0;
        $giveAsNeeded = !empty($_POST['give_as_needed']) ? 1 : 0;
        $clinic_id = (int) ($_POST['clinic_id'] ?? 0);
        $clinic_id = $clinic_id > 0 ? $clinic_id : null;

        $vet_name = trim((string) ($_POST['vet_name'] ?? ''));
        $vet_name = $vet_name !== '' ? mb_strtoupper($vet_name, 'UTF-8') : null;

        if ($dose === '') {
            http_response_code(400);
            echo h(t('error.dose_required'));
            return;
        }

        $db = DB::pdo();
        if ($createMedication) {
            $medicationName = trim((string) ($_POST['new_medication_name'] ?? ''));
            $medicationMolecule = trim((string) ($_POST['new_medication_molecule'] ?? ''));
            $medicationForm = trim((string) ($_POST['new_medication_form'] ?? ''));
            if ($medicationName === '') {
                http_response_code(400);
                echo h(t('error.medication_name_required'));
                return;
            }

            $existing = $db->prepare('SELECT id FROM ref_medications WHERE name = :name COLLATE NOCASE LIMIT 1');
            $existing->execute([':name' => $medicationName]);
            $medId = (int) ($existing->fetchColumn() ?: 0);
            if ($medId <= 0) {
                $insertMedication = $db->prepare(
                    'INSERT INTO ref_medications(name, molecule, form) VALUES(:name,:molecule,:form)',
                );
                $insertMedication->execute([
                    ':name' => mb_substr($medicationName, 0, 150),
                    ':molecule' => $medicationMolecule !== '' ? mb_substr($medicationMolecule, 0, 150) : null,
                    ':form' => $medicationForm !== '' ? mb_substr($medicationForm, 0, 100) : null,
                ]);
                $medId = (int) $db->lastInsertId();
            }
        }
        $clinicContact = self::resolveDirectoryContact(
            $db,
            (int) ($_POST['clinic_contact_id'] ?? 0),
            (string) ($_POST['new_clinic_name'] ?? ''),
            'organization',
            'cabinet',
        );
        $vetContact = self::resolveDirectoryContact(
            $db,
            (int) ($_POST['vet_contact_id'] ?? 0),
            (string) ($_POST['new_vet_name'] ?? ''),
            'person',
            'veterinaire',
        );
        self::normalizeVeterinaryClinic($db, $vetContact, $clinicContact);
        if (!$vetContact || !$clinicContact) {
            http_response_code(400);
            echo h(t('error.vet_clinic_required'));
            return;
        }
        $vet_name = $vetContact['name'] ?? $vet_name;
        $stmt = $db->prepare('
        INSERT INTO treatments(animal_id, medication_id, route, dose_text, start_date, end_date, ongoing, notes, clinic_id, vet_name, clinic_contact_id, vet_contact_id, give_morning, give_evening, give_as_needed, created_by)
        VALUES(:aid,:mid,:route,:dose,:start,:end,1,:notes,:clinic_id,:vet_name,:clinic_contact_id,:vet_contact_id,:give_morning,:give_evening,:give_as_needed,:created_by)
        ');
        $stmt->execute([
            ':aid' => $id,
            ':mid' => $medId,
            ':route' => $route !== '' ? $route : null,
            ':dose' => $dose,
            ':start' => $start,
            ':end' => $end !== '' ? $end : null,
            ':notes' => $notes !== '' ? $notes : null,
            ':clinic_id' => $clinic_id,
            ':vet_name' => $vet_name,
            ':clinic_contact_id' => $clinicContact['id'] ?? null,
            ':vet_contact_id' => $vetContact['id'] ?? null,
            ':give_morning' => $giveMorning,
            ':give_evening' => $giveEvening,
            ':give_as_needed' => $giveAsNeeded,
            ':created_by' => Auth::id(),
        ]);
        if ((int) ($_POST['tariff_id'] ?? 0) > 0) {
            PricingService::record($db, $id, (int) $_POST['tariff_id'], 1, $start, 'Ajoutée avec le traitement');
        }

        self::logHistory(
            $id,
            'treatment',
            'Traitement ajouté',
            "Médicament #$medId\nDose : $dose\nDébut : $start" .
                ($end !== '' ? "\nFin : $end" : '') .
                ($notes !== '' ? "\nNotes : $notes" : ''),
        );

        $db->prepare("UPDATE animals SET updated_at = datetime('now') WHERE id = :id")->execute([':id' => $id]);
        self::redirectToAnimal($id);
    }

    public static function addExpense(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
            http_response_code(405);
            return;
        }
        $animal = (int) ($_POST['animal_id'] ?? 0);
        $tariff = (int) ($_POST['tariff_id'] ?? 0);
        $qty = (float) str_replace(',', '.', (string) ($_POST['quantity'] ?? 1));
        $date = (string) ($_POST['occurred_on'] ?? date('Y-m-d'));
        if (!$animal || !$tariff || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
            http_response_code(400);
            echo h(t('error.invalid_expense'));
            return;
        }
        $db = DB::pdo();
        try {
            $total = PricingService::record($db, $animal, $tariff, $qty, $date, trim((string) ($_POST['notes'] ?? '')));
            self::logHistory(
                $animal,
                'medical',
                'Dépense vétérinaire ajoutée',
                number_format($total / 100, 2, ',', ' ') . ' €',
            );
            AuditService::log(
                'animal_expense_added',
                '/animal/add-expense',
                'Dépense vétérinaire enregistrée',
                'animal',
                $animal,
                ['total_cents' => $total],
            );
            self::redirectToAnimal($animal);
        } catch (Throwable $e) {
            http_response_code(400);
            echo h($e->getMessage());
        }
    }

    public static function addVaccine(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            echo h(t('error.method_not_allowed'));
            return;
        }
        $db = DB::pdo();

        $id = (int) ($_POST['animal_id'] ?? 0);
        $vacId = (int) ($_POST['vaccine_id'] ?? 0);
        if ($id <= 0 || $vacId <= 0) {
            http_response_code(400);
            echo h(t('error.bad_request'));
            return;
        }

        $done = trim((string) ($_POST['done_date'] ?? date('Y-m-d')));
        $due = trim((string) ($_POST['due_date'] ?? ''));
        // Si due_date est fourni, on respecte.
        // Sinon auto selon protocole.
        if ($due === '') {
            // Birthdate (pour l'âge)
            $stmt = $db->prepare('SELECT birth_date FROM animals WHERE id = :id');
            $stmt->execute([':id' => $id]);
            $birth = (string) ($stmt->fetchColumn() ?: '');

            $isUnderOne = false;
            if ($birth !== '') {
                $birthDt = new DateTime($birth);
                $doneDt = new DateTime($done);
                $ageDays = (int) $birthDt->diff($doneDt)->format('%r%a');
                if ($ageDays >= 0) {
                    $isUnderOne = $ageDays < 365;
                }
            }

            // Protocole vaccin (family TC / L)
            $stmt = $db->prepare('SELECT family, name FROM ref_vaccines WHERE id = :vid');
            $stmt->execute([':vid' => $vacId]);
            $vac = $stmt->fetch() ?: [];
            $family = strtoupper((string) ($vac['family'] ?? ''));

            // Détermine si c'est une primo pour la famille L (Leucose)
            $isPrimoL = false;
            if ($family === 'L') {
                $stmt = $db->prepare("
                SELECT COUNT(*)
                FROM vaccinations v
                JOIN ref_vaccines rv ON rv.id = v.vaccine_id
                WHERE v.animal_id = :aid AND upper(coalesce(rv.family,'')) = 'L'
                ");
                $stmt->execute([':aid' => $id]);
                $countL = (int) $stmt->fetchColumn();
                $isPrimoL = $countL === 0;
            }

            // Règles
            if ($family === 'L') {
                $due = $isPrimoL
                    ? date('Y-m-d', strtotime($done . ' +1 month'))
                    : date('Y-m-d', strtotime($done . ' +1 year'));
            } else {
                // TC (ou inconnu => TC)
                $due = $isUnderOne
                    ? date('Y-m-d', strtotime($done . ' +1 month'))
                    : date('Y-m-d', strtotime($done . ' +1 year'));
            }
        }

        $lot = trim((string) ($_POST['lot'] ?? ''));
        $manufacturer = trim((string) ($_POST['manufacturer'] ?? ''));
        $batchExpiresOn = trim((string) ($_POST['batch_expires_on'] ?? ''));
        if ($batchExpiresOn !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $batchExpiresOn)) {
            http_response_code(400);
            echo h(t('error.invalid_batch_expiry'));
            return;
        }
        $administratorUserId = (int) ($_POST['administered_by_user_id'] ?? 0) ?: null;
        $administratorName = trim((string) ($_POST['administered_by_name'] ?? ''));
        if ($administratorUserId) {
            $check = $db->prepare('SELECT 1 FROM users WHERE id=:id AND active=1');
            $check->execute([':id' => $administratorUserId]);
            if (!$check->fetchColumn()) {
                $administratorUserId = null;
            }
        }
        $notes = trim((string) ($_POST['notes'] ?? ''));
        $clinic_id = (int) ($_POST['clinic_id'] ?? 0);
        $clinic_id = $clinic_id > 0 ? $clinic_id : null;

        $vet_name = trim((string) ($_POST['vet_name'] ?? ''));
        $vet_name = $vet_name !== '' ? mb_strtoupper($vet_name, 'UTF-8') : null;
        $clinicContact = self::resolveDirectoryContact(
            $db,
            (int) ($_POST['clinic_contact_id'] ?? 0),
            (string) ($_POST['new_clinic_name'] ?? ''),
            'organization',
            'cabinet',
        );
        $vetContact = self::resolveDirectoryContact(
            $db,
            (int) ($_POST['vet_contact_id'] ?? 0),
            (string) ($_POST['new_vet_name'] ?? ''),
            'person',
            'veterinaire',
        );
        self::normalizeVeterinaryClinic($db, $vetContact, $clinicContact);
        if (!$vetContact || !$clinicContact) {
            http_response_code(400);
            echo h(t('error.vet_clinic_required'));
            return;
        }
        $vet_name = $vetContact['name'] ?? $vet_name;

        $stmt = $db->prepare('
        INSERT INTO vaccinations(animal_id, vaccine_id, done_date, due_date, lot,manufacturer,batch_expires_on,administered_by_user_id,administered_by_name, notes, clinic_id, vet_name, clinic_contact_id, vet_contact_id, created_by)
        VALUES(:aid,:vid,:done,:due,:lot,:manufacturer,:batch_expiry,:administrator_user,:administrator_name,:notes,:clinic_id,:vet_name,:clinic_contact_id,:vet_contact_id,:created_by)
        ');
        $stmt->execute([
            ':aid' => $id,
            ':vid' => $vacId,
            ':done' => $done,
            ':due' => $due !== '' ? $due : null,
            ':lot' => $lot !== '' ? $lot : null,
            ':manufacturer' => $manufacturer ?: null,
            ':batch_expiry' => $batchExpiresOn ?: null,
            ':administrator_user' => $administratorUserId,
            ':administrator_name' => $administratorName ?: null,
            ':notes' => $notes !== '' ? $notes : null,
            ':clinic_id' => $clinic_id,
            ':vet_name' => $vet_name,
            ':clinic_contact_id' => $clinicContact['id'] ?? null,
            ':vet_contact_id' => $vetContact['id'] ?? null,
            ':created_by' => Auth::id(),
        ]);
        InventoryService::consumeBatch(
            (int) ($_POST['inventory_batch_id'] ?? 0),
            (float) str_replace(',', '.', (string) ($_POST['inventory_quantity'] ?? 0)),
            $id,
            'Vaccination du ' . $done,
        );
        if ((int) ($_POST['tariff_id'] ?? 0) > 0) {
            PricingService::record($db, $id, (int) $_POST['tariff_id'], 1, $done, 'Ajoutée avec la vaccination');
        }

        self::logHistory(
            $id,
            'vaccine',
            'Vaccin ajouté',
            "Vaccin #$vacId\nFait le : $done" .
                ($due !== '' ? "\nRappel : $due" : '') .
                ($manufacturer !== '' ? "\nFabricant : $manufacturer" : '') .
                ($lot !== '' ? "\nLot : $lot" : '') .
                ($batchExpiresOn !== '' ? "\nExpiration du lot : $batchExpiresOn" : '') .
                ($administratorName !== '' || $administratorUserId
                    ? "\nAdministré par : " .
                        ($administratorName !== '' ? $administratorName : 'utilisateur #' . $administratorUserId)
                    : ''),
        );

        $db->prepare("UPDATE animals SET updated_at = datetime('now') WHERE id = :id")->execute([':id' => $id]);
        self::redirectToAnimal($id);
    }
}
