<?php

declare(strict_types=1);

final class CareRoundsController
{
    public static function index(): void
    {
        $db = DB::pdo();
        $period = ($_GET['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
        $date = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) ($_GET['date'] ?? ''))
            ? (string) $_GET['date']
            : date('Y-m-d');
        $stmt = $db->prepare('SELECT * FROM care_rounds WHERE round_date=:date AND period=:period');
        $stmt->execute([':date' => $date, ':period' => $period]);
        $round = $stmt->fetch();
        if (!$round) {
            $round = ['id' => 0, 'round_date' => $date, 'period' => $period, 'performed_by_user_id' => null];
        }
        $layouts = [];
        foreach ($db->query('SELECT * FROM care_room_layouts')->fetchAll() as $l) {
            $layouts[$l['room']] = $l;
        }
        $animals = $db
            ->query(
                "
        SELECT a.*, p.filename AS primary_photo
        FROM animals a LEFT JOIN animal_photos p ON p.animal_id=a.id AND p.is_primary=1
        WHERE a.deleted_at IS NULL AND a.archived_at IS NULL
          AND lower(a.status) IN ('refuge','soin','quarantaine')
        ORDER BY a.name COLLATE NOCASE
        ",
            )
            ->fetchAll();
        $observations = [];
        $administrations = [];
        if ((int) $round['id'] > 0) {
            $stmt = $db->prepare('SELECT * FROM care_round_observations WHERE round_id=:id');
            $stmt->execute([':id' => $round['id']]);
            foreach ($stmt->fetchAll() as $o) {
                $observations[(int) $o['animal_id']] = $o;
            }
            $stmt = $db->prepare('SELECT treatment_id,status FROM treatment_administrations WHERE round_id=:id');
            $stmt->execute([':id' => $round['id']]);
            foreach ($stmt->fetchAll() as $a) {
                $administrations[(int) $a['treatment_id']] = $a['status'];
            }
        }
        $treatments = [];
        $stmt = $db->prepare("
        SELECT t.*,m.name AS medication_name FROM treatments t JOIN ref_medications m ON m.id=t.medication_id
        WHERE t.ongoing=1 AND ((:period='morning' AND t.give_morning=1) OR (:period='evening' AND t.give_evening=1))
        ORDER BY m.name
        ");
        $stmt->execute([':period' => $period]);
        foreach ($stmt->fetchAll() as $t) {
            $treatments[(int) $t['animal_id']][] = $t;
        }
        $unconfigured = [];
        $rows = $db
            ->query(
                'SELECT t.id,t.animal_id,t.dose_text,m.name medication_name FROM treatments t JOIN ref_medications m ON m.id=t.medication_id WHERE t.ongoing=1 AND t.give_morning=0 AND t.give_evening=0 AND t.give_as_needed=0 ORDER BY m.name',
            )
            ->fetchAll();
        foreach ($rows as $r) {
            $unconfigured[(int) $r['animal_id']][] = $r;
        }
        render('care_round.php', [
            'title' => t('care.title'),
            'pageDescription' => t('care.description'),
            'period' => $period,
            'date' => $date,
            'round' => $round,
            'layouts' => $layouts,
            'rooms' => ShelterRoomService::all(true),
            'animals' => $animals,
            'treatmentsByAnimal' => $treatments,
            'unconfiguredTreatments' => $unconfigured,
            'observations' => $observations,
            'administrations' => $administrations,
        ]);
    }

    public static function saveObservation(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            return;
        }
        $db = DB::pdo();
        $roundId = self::ensureRound($db);
        $animalId = (int) ($_POST['animal_id'] ?? 0);
        if (!$roundId || !$animalId) {
            http_response_code(400);
            return;
        }
        $animalCheck = $db->prepare(
            "SELECT 1 FROM animals WHERE id=? AND deleted_at IS NULL AND archived_at IS NULL AND lower(status) IN ('refuge','soin','quarantaine')",
        );
        $animalCheck->execute([$animalId]);
        if (!$animalCheck->fetchColumn()) {
            http_response_code(404);
            return;
        }
        $allowed = [
            'food' => ['normal', 'little', 'refused'],
            'water' => ['normal', 'renewed', 'abnormal'],
            'urine' => ['normal', 'absent', 'abnormal'],
            'stool' => ['normal', 'absent', 'abnormal'],
            'general_state' => ['good', 'watch', 'alert'],
        ];
        $values = [];
        foreach ($allowed as $field => $opts) {
            $value = (string) ($_POST[$field] ?? $opts[0]);
            $values[$field] = in_array($value, $opts, true) ? $value : $opts[0];
        }
        $db->prepare(
            'UPDATE care_rounds SET performed_by_user_id=COALESCE(performed_by_user_id,:user) WHERE id=:id',
        )->execute([':user' => Auth::id(), ':id' => $roundId]);
        $comment = trim((string) ($_POST['comment'] ?? ''));
        $photoComment = trim((string) ($_POST['photo_comment'] ?? ''));
        $photo = $_FILES['care_photo'] ?? null;
        $hasPhoto = is_array($photo) && ($photo['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE;
        if ($hasPhoto) {
            if (
                ($photo['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK ||
                !is_uploaded_file((string) $photo['tmp_name']) ||
                ($photo['size'] ?? 0) > 20 * 1024 * 1024 ||
                $photoComment === ''
            ) {
                http_response_code(400);
                echo h(t('care.invalid_photo'));
                return;
            }
            $imageInfo = @getimagesize((string) $photo['tmp_name']);
            $mime = (string) ($imageInfo['mime'] ?? '');
            $extensions = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', 'image/gif' => 'gif'];
            if (!isset($extensions[$mime])) {
                http_response_code(400);
                echo h(t('care.invalid_photo_format'));
                return;
            }
        }
        $admins = is_array($_POST['treatments'] ?? null) ? $_POST['treatments'] : [];
        $skippedIds = [];
        foreach ($admins as $treatmentId => $status) {
            if ($status === 'skipped') {
                $skippedIds[] = (int) $treatmentId;
            }
        }
        $alerts = [];
        if ($values['food'] === 'little') {
            $alerts[] = 'A peu mangé';
        } elseif ($values['food'] === 'refused') {
            $alerts[] = 'A refusé de manger';
        }
        if ($values['water'] === 'abnormal') {
            $alerts[] = 'Eau anormale';
        }
        if ($values['urine'] === 'absent') {
            $alerts[] = 'Urines absentes';
        } elseif ($values['urine'] === 'abnormal') {
            $alerts[] = 'Urines anormales';
        }
        if ($values['stool'] === 'absent') {
            $alerts[] = 'Selles absentes';
        } elseif ($values['stool'] === 'abnormal') {
            $alerts[] = 'Selles anormales';
        }
        if ($values['general_state'] === 'watch') {
            $alerts[] = 'État général à surveiller';
        } elseif ($values['general_state'] === 'alert') {
            $alerts[] = 'Alerte sur l’état général';
        }
        if ($comment !== '') {
            $alerts[] = 'Commentaire : ' . $comment;
        }
        foreach ($skippedIds as $treatmentId) {
            $nameStmt = $db->prepare(
                'SELECT m.name FROM treatments t LEFT JOIN ref_medications m ON m.id=t.medication_id WHERE t.id=:id AND t.animal_id=:animal',
            );
            $nameStmt->execute([':id' => $treatmentId, ':animal' => $animalId]);
            $alerts[] = 'Traitement non donné : ' . ($nameStmt->fetchColumn() ?: 'traitement #' . $treatmentId);
        }
        $signature = $alerts
            ? hash('sha256', json_encode([$values, $comment, $skippedIds], JSON_UNESCAPED_UNICODE))
            : null;
        $previousStmt = $db->prepare(
            'SELECT alert_signature FROM care_round_observations WHERE round_id=:round AND animal_id=:animal',
        );
        $previousStmt->execute([':round' => $roundId, ':animal' => $animalId]);
        $previousSignature = $previousStmt->fetchColumn() ?: null;

        $savedPhotoPath = null;
        $db->beginTransaction();
        try {
            $stmt = $db->prepare(
                "INSERT INTO care_round_observations(round_id,animal_id,food,water,urine,stool,general_state,comment,alert_signature) VALUES(:round,:animal,:food,:water,:urine,:stool,:general,:comment,:signature) ON CONFLICT(round_id,animal_id) DO UPDATE SET food=excluded.food,water=excluded.water,urine=excluded.urine,stool=excluded.stool,general_state=excluded.general_state,comment=excluded.comment,alert_signature=excluded.alert_signature,checked_at=datetime('now')",
            );
            $stmt->execute([
                ':round' => $roundId,
                ':animal' => $animalId,
                ':food' => $values['food'],
                ':water' => $values['water'],
                ':urine' => $values['urine'],
                ':stool' => $values['stool'],
                ':general' => $values['general_state'],
                ':comment' => $comment ?: null,
                ':signature' => $signature,
            ]);
            $ins = $db->prepare(
                "INSERT INTO treatment_administrations(round_id,treatment_id,animal_id,status,comment) VALUES(:round,:treatment,:animal,:status,:comment) ON CONFLICT(round_id,treatment_id) DO UPDATE SET status=excluded.status,comment=excluded.comment,administered_at=datetime('now')",
            );
            $treatmentCheck = $db->prepare('SELECT 1 FROM treatments WHERE id=? AND animal_id=? AND ongoing=1');
            foreach ($admins as $treatmentId => $status) {
                if (!in_array($status, ['given', 'skipped'], true)) {
                    continue;
                }
                $treatmentCheck->execute([(int) $treatmentId, $animalId]);
                if (!$treatmentCheck->fetchColumn()) {
                    continue;
                }
                $ins->execute([
                    ':round' => $roundId,
                    ':treatment' => (int) $treatmentId,
                    ':animal' => $animalId,
                    ':status' => $status,
                    ':comment' => null,
                ]);
            }
            if ($hasPhoto) {
                $dir = PrivateMediaService::animalDir($animalId);
                PrivateMediaService::ensure($dir);
                $stored = ImageService::storeUploaded((string) $photo['tmp_name'], $dir, 'tournee', 'medical');
                $filename = $stored['filename'];
                $savedPhotoPath = $stored['path'];
                $mime = $stored['mime'];
                @chmod($savedPhotoPath, 0600);
                $photoStmt = $db->prepare(
                    'INSERT INTO animal_photos(animal_id,filename,original_name,mime,size_bytes,is_primary,is_public,caption,care_round_id) VALUES(:animal,:filename,:original,:mime,:size,0,0,:caption,:round)',
                );
                $photoStmt->execute([
                    ':animal' => $animalId,
                    ':filename' => $filename,
                    ':original' => (string) ($photo['name'] ?? 'photo'),
                    ':mime' => $mime,
                    ':size' => $stored['size'],
                    ':caption' => $photoComment,
                    ':round' => $roundId,
                ]);
                $alerts[] = 'Photo privée ajoutée : ' . $photoComment;
            }
            if (($signature !== null && $signature !== $previousSignature) || $hasPhoto) {
                $roundStmt = $db->prepare('SELECT round_date,period FROM care_rounds WHERE id=:id');
                $roundStmt->execute([':id' => $roundId]);
                $roundInfo = $roundStmt->fetch();
                $roundDate = $roundInfo ? date('d/m/Y', strtotime((string) $roundInfo['round_date'])) : date('d/m/Y');
                $roundPeriod = ($roundInfo['period'] ?? 'morning') === 'evening' ? 'soir' : 'matin';
                $history = $db->prepare(
                    "INSERT INTO animal_history(animal_id,type,label,details,user_id) VALUES(:animal,'care_round',:label,:details,:user)",
                );
                $history->execute([
                    ':animal' => $animalId,
                    ':label' => 'Anomalie durant la tournée du ' . $roundPeriod . ' — ' . $roundDate,
                    ':details' => implode("\n", $alerts),
                    ':user' => Auth::id(),
                ]);
            }
            $db->prepare("UPDATE animals SET updated_at=datetime('now') WHERE id=:id")->execute([':id' => $animalId]);
            $db->commit();
        } catch (Throwable $e) {
            if ($db->inTransaction()) {
                $db->rollBack();
            }
            if ($savedPhotoPath && is_file($savedPhotoPath)) {
                @unlink($savedPhotoPath);
            }
            throw $e;
        }
        $period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
        $date = (string) ($_POST['date'] ?? date('Y-m-d'));
        header('Location: /care-round?period=' . $period . '&date=' . rawurlencode($date) . '#animal-' . $animalId);
        exit();
    }

    public static function saveRoom(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            return;
        }
        $db = DB::pdo();
        $roundId = self::ensureRound($db);
        $room = (string) ($_POST['room'] ?? '');
        $rooms = ShelterRoomService::byCode(true);
        if (!$roundId || !isset($rooms[$room]) || empty($rooms[$room]['bulk_validation'])) {
            http_response_code(400);
            return;
        }

        $animals = $db
            ->query(
                "SELECT id,status,refuge_room FROM animals WHERE deleted_at IS NULL AND archived_at IS NULL AND lower(status) IN ('refuge','soin','quarantaine')",
            )
            ->fetchAll(PDO::FETCH_ASSOC);
        $stmt = $db->prepare(
            "INSERT OR IGNORE INTO care_round_observations(round_id,animal_id,food,water,urine,stool,general_state) VALUES(:round,:animal,'normal','normal','normal','normal','good')",
        );
        foreach ($animals as $animal) {
            if (ShelterRoomService::resolveAnimal($animal, $rooms) === $room) {
                $stmt->execute([':round' => $roundId, ':animal' => $animal['id']]);
            }
        }

        $period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
        $date = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) ($_POST['date'] ?? ''))
            ? (string) $_POST['date']
            : date('Y-m-d');
        header('Location: /care-round?period=' . $period . '&date=' . rawurlencode($date) . '#room-' . $room);
        exit();
    }

    public static function setup(): void
    {
        $db = DB::pdo();
        $layouts = [];
        foreach ($db->query('SELECT * FROM care_room_layouts')->fetchAll() as $l) {
            $layouts[$l['room']] = $l;
        }
        $animals = $db
            ->query(
                "SELECT id,name,status,refuge_room,care_box_key,current_address FROM animals WHERE deleted_at IS NULL AND archived_at IS NULL AND lower(status) IN ('refuge','soin','quarantaine') ORDER BY name COLLATE NOCASE",
            )
            ->fetchAll();
        render('care_setup.php', [
            'title' => t('care.setup_title'),
            'layouts' => $layouts,
            'rooms' => ShelterRoomService::all(true),
            'animals' => $animals,
        ]);
    }

    public static function saveSetup(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            return;
        }
        $db = DB::pdo();
        $topOpts = ['split', 'left2', 'right2', 'merged'];
        $bottomOpts = ['split', 'merged'];
        foreach (['care', 'quarantine'] as $room) {
            $top = (string) ($_POST[$room . '_top'] ?? 'split');
            $bottom = (string) ($_POST[$room . '_bottom'] ?? 'split');
            if (!in_array($top, $topOpts, true)) {
                $top = 'split';
            }
            if (!in_array($bottom, $bottomOpts, true)) {
                $bottom = 'split';
            }
            $db->prepare(
                "UPDATE care_room_layouts SET top_layout=:top,bottom_layout=:bottom,updated_at=datetime('now') WHERE room=:room",
            )->execute([':top' => $top, ':bottom' => $bottom, ':room' => $room]);
        }
        $assignments = is_array($_POST['box'] ?? null) ? $_POST['box'] : [];
        $roomAssignments = is_array($_POST['room'] ?? null) ? $_POST['room'] : [];
        $rooms = ShelterRoomService::byCode(true);
        $locationReason = trim((string) ($_POST['location_change_reason'] ?? ''));
        $select = $db->prepare(
            'SELECT status,refuge_room,care_box_key,current_address FROM animals WHERE id=:id AND deleted_at IS NULL AND archived_at IS NULL',
        );
        $stmt = $db->prepare(
            "UPDATE animals SET status=:status,refuge_room=:room,care_box_key=:box,updated_at=datetime('now') WHERE id=:id AND deleted_at IS NULL AND archived_at IS NULL",
        );
        foreach ($roomAssignments as $id => $roomCode) {
            $animalId = (int) $id;
            $select->execute([':id' => $animalId]);
            $before = $select->fetch(PDO::FETCH_ASSOC);
            if (!$before) {
                continue;
            }
            $roomCode = (string) $roomCode;
            if (!isset($rooms[$roomCode])) {
                continue;
            }
            $room = $rooms[$roomCode];
            $allowedBoxes = array_column(
                array_filter($room['boxes'], static fn($box) => (int) $box['active'] === 1),
                'code',
            );
            if (in_array($roomCode, ['care', 'quarantine'], true)) {
                $allowedBoxes = array_values(
                    array_unique(array_merge($allowedBoxes, ['top_ab', 'top_bc', 'top_all', 'bottom_all'])),
                );
            }
            $newBox = trim((string) ($assignments[$id] ?? ''));
            if ($room['room_type'] !== 'boxes' || !in_array($newBox, $allowedBoxes, true)) {
                $newBox = null;
            }
            $status = (string) $room['status_code'];
            $stmt->execute([':status' => $status, ':room' => $roomCode, ':box' => $newBox, ':id' => $animalId]);
            LocationHistoryService::record(
                $db,
                $animalId,
                $before,
                array_merge($before, ['status' => $status, 'refuge_room' => $roomCode, 'care_box_key' => $newBox]),
                'care_setup',
                $locationReason ?: 'Attribution de la salle ou du box depuis la tournée',
            );
        }
        header('Location: /care-round');
        exit();
    }

    public static function saveTreatmentSchedule(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            http_response_code(405);
            return;
        }
        $id = (int) ($_POST['treatment_id'] ?? 0);
        $schedule = (string) ($_POST['schedule'] ?? '');
        $values = match ($schedule) {
            'morning' => [1, 0, 0],
            'evening' => [0, 1, 0],
            'both' => [1, 1, 0],
            'needed' => [0, 0, 1],
            default => null,
        };
        if (!$id || !$values) {
            http_response_code(400);
            return;
        }
        DB::pdo()
            ->prepare('UPDATE treatments SET give_morning=?,give_evening=?,give_as_needed=? WHERE id=?')
            ->execute([$values[0], $values[1], $values[2], $id]);
        $period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
        header('Location: /care-round?period=' . $period);
        exit();
    }

    private static function ensureRound(PDO $db): int
    {
        $date = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) ($_POST['date'] ?? ''))
            ? (string) $_POST['date']
            : date('Y-m-d');
        $period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
        $requested = (int) ($_POST['round_id'] ?? 0);
        if ($requested) {
            $s = $db->prepare('SELECT id FROM care_rounds WHERE id=? AND round_date=? AND period=?');
            $s->execute([$requested, $date, $period]);
            if ($s->fetchColumn()) {
                return $requested;
            }
        }
        $db->prepare(
            'INSERT OR IGNORE INTO care_rounds(round_date,period,performed_by_user_id) VALUES(?,?,?)',
        )->execute([$date, $period, Auth::id()]);
        $s = $db->prepare('SELECT id FROM care_rounds WHERE round_date=? AND period=?');
        $s->execute([$date, $period]);
        return (int) $s->fetchColumn();
    }
}
