<?php
declare(strict_types=1);
namespace App\Repositories;
use App\Core\Repository;

final class QueueRepository extends Repository {
    public function ensureOpenSession(): int {
        $session = $this->one("SELECT * FROM queue_sessions WHERE status='open' ORDER BY id DESC LIMIT 1");
        if ($session) return (int)$session['id'];
        return $this->insert("INSERT INTO queue_sessions(session_date,status,created_at) VALUES(CURDATE(),'open',NOW())");
    }

    public function issueMany(int $count, string $prefix='A'): array {
        $sid = $this->ensureOpenSession();
        $mx = (int)($this->one("SELECT MAX(seq_no) mx FROM queue_tickets WHERE queue_session_id=?", [$sid])['mx'] ?? 0);
        $out = [];
        for ($i=1; $i <= $count; $i++) {
            $seq = $mx + $i;
            $ticketNo = strtoupper($prefix) . str_pad((string)$seq, 3, '0', STR_PAD_LEFT);
            $token = bin2hex(random_bytes(16));
            $id = $this->insert("INSERT INTO queue_tickets(queue_session_id,seq_no,ticket_no,public_token,ticket_status,created_at,updated_at) VALUES(?,?,?,?, 'pending', NOW(), NOW())", [$sid, $seq, $ticketNo, $token]);
            $out[] = ['id' => $id, 'ticket_no' => $ticketNo, 'public_token' => $token, 'seq_no' => $seq];
        }
        return $out;
    }

    public function findByToken(string $token): ?array {
        return $this->one("SELECT qt.*, qs.session_date FROM queue_tickets qt JOIN queue_sessions qs ON qs.id=qt.queue_session_id WHERE qt.public_token=? LIMIT 1", [$token]);
    }

    public function savePhoneIfEmpty(int $ticketId, string $phone): void {
        $this->exec("UPDATE queue_tickets SET customer_phone=?, updated_at=NOW() WHERE id=? AND (customer_phone IS NULL OR customer_phone='')", [$phone, $ticketId]);
    }

    public function countBefore(int $sessionId, int $seqNo): int {
        return (int)$this->one("SELECT COUNT(*) c FROM queue_tickets WHERE queue_session_id=? AND ticket_status='pending' AND seq_no < ?", [$sessionId, $seqNo])['c'];
    }

    public function snapshot(): array {
        $sid = $this->ensureOpenSession();
        return [
            'session' => $this->one("SELECT * FROM queue_sessions WHERE id=?", [$sid]),
            'serving' => $this->all("SELECT * FROM queue_tickets WHERE queue_session_id=? AND ticket_status='serving' ORDER BY called_at DESC, id DESC LIMIT 6", [$sid]),
            'waiting' => $this->all("SELECT * FROM queue_tickets WHERE queue_session_id=? AND ticket_status='pending' ORDER BY seq_no ASC LIMIT 25", [$sid]),
            'skipped' => $this->all("SELECT * FROM queue_tickets WHERE queue_session_id=? AND ticket_status='skipped' ORDER BY updated_at DESC, id DESC LIMIT 20", [$sid]),
            'recent' => $this->all("SELECT * FROM queue_tickets WHERE queue_session_id=? AND ticket_status IN('serving','skipped') ORDER BY updated_at DESC, id DESC LIMIT 12", [$sid]),
            'totals' => [
                'issued' => (int)($this->one("SELECT COUNT(*) c FROM queue_tickets WHERE queue_session_id=?", [$sid])['c'] ?? 0),
                'done' => (int)($this->one("SELECT COUNT(*) c FROM queue_tickets WHERE queue_session_id=? AND ticket_status='done'", [$sid])['c'] ?? 0),
                'waiting' => (int)($this->one("SELECT COUNT(*) c FROM queue_tickets WHERE queue_session_id=? AND ticket_status='pending'", [$sid])['c'] ?? 0),
                'skipped' => (int)($this->one("SELECT COUNT(*) c FROM queue_tickets WHERE queue_session_id=? AND ticket_status='skipped'", [$sid])['c'] ?? 0),
            ]
        ];
    }

    public function getCurrentServing(): ?array {
        $sid = $this->ensureOpenSession();
        return $this->one("SELECT * FROM queue_tickets WHERE queue_session_id=? AND ticket_status='serving' ORDER BY called_at DESC, id DESC LIMIT 1", [$sid]);
    }

    public function getNextPending(): ?array {
        $sid = $this->ensureOpenSession();
        return $this->one("SELECT * FROM queue_tickets WHERE queue_session_id=? AND ticket_status='pending' ORDER BY seq_no ASC LIMIT 1", [$sid]);
    }

    public function markServing(int $id): void {
        $this->exec("UPDATE queue_tickets SET ticket_status='serving', called_at=NOW(), updated_at=NOW() WHERE id=?", [$id]);
    }

    public function markDone(int $id, float $amount=0.0): void {
        // amount is used in reports dynamically via settings; kept for future payload compatibility
        $this->exec("UPDATE queue_tickets SET ticket_status='done', updated_at=NOW() WHERE id=?", [$id]);
    }

    public function markSkipped(int $id): void {
        $this->exec("UPDATE queue_tickets SET ticket_status='skipped', updated_at=NOW() WHERE id=?", [$id]);
    }

    public function markPending(int $id): void {
        $this->exec("UPDATE queue_tickets SET ticket_status='pending', updated_at=NOW() WHERE id=?", [$id]);
    }

    public function callNext(): ?array {
        $this->db->beginTransaction();
        try {
            $current = $this->getCurrentServing();
            if ($current) {
                $this->markDone((int)$current['id']);
            }
            $next = $this->getNextPending();
            if ($next) {
                $this->markServing((int)$next['id']);
                $next = $this->one("SELECT * FROM queue_tickets WHERE id=?", [(int)$next['id']]);
            }
            $this->db->commit();
            return $next ?: null;
        } catch (\Throwable $e) {
            $this->db->rollBack();
            throw $e;
        }
    }

    public function markCurrentDone(): ?array {
        $current = $this->getCurrentServing();
        if ($current) {
            $this->markDone((int)$current['id']);
        }
        return $current;
    }

    public function skipCurrent(): ?array {
        $current = $this->getCurrentServing();
        if ($current) {
            $this->markSkipped((int)$current['id']);
        }
        return $current;
    }

    public function recallSkipped(int $ticketId): ?array {
        $this->db->beginTransaction();
        try {
            $ticket = $this->one("SELECT * FROM queue_tickets WHERE id=? AND ticket_status='skipped' LIMIT 1", [$ticketId]);
            if (!$ticket) {
                $this->db->commit();
                return null;
            }
            $current = $this->getCurrentServing();
            if ($current) {
                $this->markPending((int)$current['id']);
            }
            $this->markServing((int)$ticket['id']);
            $updated = $this->one("SELECT * FROM queue_tickets WHERE id=?", [$ticketId]);
            $this->db->commit();
            return $updated ?: null;
        } catch (\Throwable $e) {
            $this->db->rollBack();
            throw $e;
        }
    }

    public function resetOpenSession(): void {
        $sid = $this->ensureOpenSession();
        $this->exec("UPDATE queue_tickets SET ticket_status='skipped', updated_at=NOW() WHERE queue_session_id=? AND ticket_status IN('pending','serving')", [$sid]);
        $this->exec("UPDATE queue_sessions SET status='closed' WHERE id=?", [$sid]);
    }

    public function ticketsNeedingReminder(int $threshold): array {
        $current = $this->getCurrentServing();
        if (!$current) return [];
        $sessionId = (int)$current['queue_session_id'];
        $currentSeq = (int)$current['seq_no'];
        return $this->all("SELECT qt.*,
                (qt.seq_no - ?) AS remaining_count
            FROM queue_tickets qt
            WHERE qt.queue_session_id=?
              AND qt.ticket_status='pending'
              AND qt.customer_phone IS NOT NULL AND qt.customer_phone<>''
              AND (qt.seq_no - ?) = ?", [$currentSeq, $sessionId, $currentSeq, $threshold]);
    }

    public function notificationAlreadySent(int $ticketId, int $remaining, string $templateHash): bool {
        $row = $this->one("SELECT id FROM queue_notifications_log WHERE queue_ticket_id=? AND remaining_count=? AND template_hash=? LIMIT 1", [$ticketId, $remaining, $templateHash]);
        return (bool)$row;
    }

    public function logNotification(int $ticketId, string $phone, int $remaining, string $message, string $status, ?string $responseBody=null): void {
        $this->insert("INSERT INTO queue_notifications_log(queue_ticket_id,phone,message_body,remaining_count,template_hash,send_status,response_body,created_at)
            VALUES(?,?,?,?,?,?,?,NOW())", [$ticketId, $phone, $message, $remaining, sha1($message), $status, $responseBody]);
    }
}
