<?php

declare(strict_types=1);

final class Asm3ClinicalImporter
{
    public static function run(string $path, PDO $db, bool $apply = false, bool $createBackup = true): array
    {
        $hash = hash_file('sha256', $path);
        $src = Asm3Analyzer::rows($path, ['animalvaccination', 'vaccinationtype', 'log', 'logtype']);
        $links = self::animalLinks($db, $hash);
        $types = [];
        foreach ($src['vaccinationtype'] ?? [] as $r) {
            $types[(int) $r['ID']] = trim((string) $r['VACCINATIONTYPE']);
        }
        $logTypes = [];
        foreach ($src['logtype'] ?? [] as $r) {
            $logTypes[(int) $r['ID']] = trim((string) $r['LOGTYPENAME']);
        }
        $result = [
            'mode' => $apply ? 'apply' : 'dry-run',
            'read_only' => !$apply,
            'vaccinations' => 0,
            'medical_notes' => 0,
            'weights' => 0,
            'unresolved' => 0,
            'already_imported' => 0,
            'backup' => null,
        ];
        foreach ($src['animalvaccination'] ?? [] as $r) {
            $result['vaccinations']++;
            if (!isset($links[(int) $r['ANIMALID']])) {
                $result['unresolved']++;
            }
        }
        foreach ($src['log'] ?? [] as $r) {
            if ((int) ($r['LINKTYPE'] ?? -1) === 0) {
                if (!isset($links[(int) $r['LINKID']])) {
                    $result['unresolved']++;
                }
                $name = mb_strtolower($logTypes[(int) $r['LOGTYPEID']] ?? '');
                if ($name === 'poids') {
                    $result['weights']++;
                } else {
                    $result['medical_notes']++;
                }
            }
        }
        if (!$apply) {
            return $result;
        }
        if ($createBackup) {
            $b = BackupService::create('pre-asm3-import');
            $result['backup'] = $b['name'] ?? null;
        }
        $ownsTransaction = !$db->inTransaction();
        if ($ownsTransaction) {
            $db->beginTransaction();
        }
        try {
            $runId = self::runId($db, $path, $hash, $result['backup'], 'médical');
            foreach ($src['animalvaccination'] ?? [] as $r) {
                $animal = $links[(int) $r['ANIMALID']] ?? 0;
                if (!$animal) {
                    continue;
                }
                $source = (int) $r['ID'];
                if (self::seen($db, $hash, 'vaccination', $source)) {
                    $result['already_imported']++;
                    continue;
                }
                $name = $types[(int) $r['VACCINATIONID']] ?? 'Vaccin ASM3 ' . (int) $r['VACCINATIONID'];
                $db->prepare('INSERT OR IGNORE INTO ref_vaccines(name) VALUES(:name)')->execute([':name' => $name]);
                $q = $db->prepare('SELECT id FROM ref_vaccines WHERE name=:name');
                $q->execute([':name' => $name]);
                $vaccine = (int) $q->fetchColumn();
                $db->prepare(
                    'INSERT INTO vaccinations(animal_id,vaccine_id,done_date,due_date,lot,notes,manufacturer,batch_expires_on,administered_by_name) VALUES(:animal,:vaccine,:done,:due,:lot,:notes,:manufacturer,:expires,:by)',
                )->execute([
                    ':animal' => $animal,
                    ':vaccine' => $vaccine,
                    ':done' =>
                        self::date($r['DATEOFVACCINATION'] ?? null) ?:
                        self::date($r['DATEREQUIRED'] ?? null) ?:
                        date('Y-m-d'),
                    ':due' => self::date($r['DATEREQUIRED'] ?? null),
                    ':lot' => trim((string) $r['BATCHNUMBER']) ?: null,
                    ':notes' => trim((string) $r['COMMENTS']) ?: null,
                    ':manufacturer' => trim((string) $r['MANUFACTURER']) ?: null,
                    ':expires' => self::date($r['BATCHEXPIRYDATE'] ?? null),
                    ':by' => trim((string) $r['GIVENBY']) ?: null,
                ]);
                self::link($db, $hash, 'vaccination', $source, (int) $db->lastInsertId(), $runId);
            }
            foreach ($src['log'] ?? [] as $r) {
                if ((int) ($r['LINKTYPE'] ?? -1) !== 0) {
                    continue;
                }
                $animal = $links[(int) $r['LINKID']] ?? 0;
                if (!$animal) {
                    continue;
                }
                $source = (int) $r['ID'];
                $name = $logTypes[(int) $r['LOGTYPEID']] ?? 'Historique';
                $entity = mb_strtolower($name) === 'poids' ? 'measurement' : 'medical-log';
                if (self::seen($db, $hash, $entity, $source)) {
                    continue;
                }
                $text = trim((string) $r['COMMENTS']);
                $date = self::date($r['DATE'] ?? null) ?: date('Y-m-d');
                if ($entity === 'measurement' && preg_match('/([0-9]+(?:[.,][0-9]+)?)/', $text, $m)) {
                    $value = (float) str_replace(',', '.', $m[1]);
                    $db->prepare(
                        "INSERT INTO measurements(animal_id,measured_at,type,value,unit,notes) VALUES(:animal,:date,'weight',:value,'kg',:notes)",
                    )->execute([':animal' => $animal, ':date' => $date, ':value' => $value, ':notes' => $text]);
                } else {
                    $db->prepare(
                        "INSERT INTO medical_notes(animal_id,noted_at,kind,reason,plan) VALUES(:animal,:date,'autre',:reason,:plan)",
                    )->execute([':animal' => $animal, ':date' => $date, ':reason' => $name, ':plan' => $text ?: null]);
                }
                self::link($db, $hash, $entity, $source, (int) $db->lastInsertId(), $runId);
            }
            if ($ownsTransaction) {
                $db->commit();
            }
        } catch (Throwable $e) {
            if ($ownsTransaction && $db->inTransaction()) {
                $db->rollBack();
            }
            throw $e;
        }
        return $result;
    }
    private static function animalLinks(PDO $db, string $hash): array
    {
        $s = $db->prepare(
            "SELECT source_id,target_id FROM asm3_import_links WHERE source_sha256=:h AND entity_type='animal'",
        );
        $s->execute([':h' => $hash]);
        $o = [];
        foreach ($s as $r) {
            $o[(int) $r['source_id']] = (int) $r['target_id'];
        }
        return $o;
    }
    private static function runId(PDO $db, string $p, string $h, ?string $b, string $label): int
    {
        $db->prepare(
            "INSERT INTO asm3_import_runs(source_name,source_sha256,status,summary_json,backup_name) VALUES(:n,:h,'completed','{}',:b)",
        )->execute([':n' => basename($p) . ' · ' . $label, ':h' => $h, ':b' => $b]);
        return (int) $db->lastInsertId();
    }
    private static function seen(PDO $db, string $h, string $e, int $s): bool
    {
        $q = $db->prepare('SELECT 1 FROM asm3_import_links WHERE source_sha256=:h AND entity_type=:e AND source_id=:s');
        $q->execute([':h' => $h, ':e' => $e, ':s' => $s]);
        return (bool) $q->fetchColumn();
    }
    private static function link(PDO $db, string $h, string $e, int $s, int $t, int $r): void
    {
        $db->prepare(
            'INSERT INTO asm3_import_links(source_sha256,entity_type,source_id,target_id,import_run_id) VALUES(:h,:e,:s,:t,:r)',
        )->execute([':h' => $h, ':e' => $e, ':s' => $s, ':t' => $t, ':r' => $r]);
    }
    private static function date(mixed $v): ?string
    {
        return $v && preg_match('/^\d{4}-\d{2}-\d{2}/', (string) $v, $m) ? $m[0] : null;
    }
}
