<?php

namespace App\Controllers\View;

use App\Controllers\BaseController;
use App\Models\ClassSectionModel;
use App\Models\ConfigurationModel;
use App\Models\CertificateRecordModel;

class CertificateController extends BaseController
{
    protected $classSectionModel;
    protected $configModel;
    protected $certRecordModel;
    protected $schoolYear;

    public function __construct()
    {
        $this->classSectionModel = new ClassSectionModel();
        $this->configModel       = new ConfigurationModel();
        $this->certRecordModel   = new CertificateRecordModel();
        $this->schoolYear        = $this->configModel->getConfig('school_year');
    }

    // ─── Admin UI ──────────────────────────────────────────────────────────────

    public function index()
    {
        $db           = \Config\Database::connect();
        $selectedCsid = $this->request->getGet('class_section_id');
        $schoolYear   = $this->request->getGet('school_year') ?? $this->schoolYear;

        // ── All enrolled students across every class section ───────────────────
        $allEnrolled = $db->table('student_class sc')
            ->select('s.id AS student_id, s.firstname, s.lastname, sc.class_section_id, cs.class_section_name')
            ->join('students s', 's.id = sc.student_id')
            ->join('classSection cs', 'cs.class_section_id = sc.class_section_id')
            ->where('s.is_active', 1)
            ->where('sc.school_year', $schoolYear)
            ->orderBy('s.firstname', 'ASC')
            ->orderBy('s.lastname', 'ASC')
            ->get()
            ->getResultArray();

        $allIds = array_values(array_unique(array_map('intval', array_column($allEnrolled, 'student_id'))));

        // ── Saved generated YEAR decisions from student_decisions ──────────────
        //
        // Source of truth:
        // student_decisions.year_score
        // student_decisions.decision
        //
        // Do NOT recalculate certificate decisions here from semester_scores.
        $decisionsByStudent = [];

        if (!empty($allIds)) {
            $decisionRows = $db->table('student_decisions')
                ->whereIn('student_id', $allIds)
                ->where('school_year', $schoolYear)
                ->get()
                ->getResultArray();

            foreach ($decisionRows as $d) {
                $sid = (int)($d['student_id'] ?? 0);

                if ($sid <= 0) {
                    continue;
                }

                $decision = trim((string)($d['decision'] ?? ''));
                $source   = trim((string)($d['source'] ?? ''));

                if ($source === '') {
                    $source = $decision === '' ? 'pending' : 'manual';
                }

                $decisionsByStudent[$sid]['Year'] = [
                    'decision'   => $decision,
                    'source'     => $source,
                    'notes'      => (string)($d['notes'] ?? ''),
                    'year_score' => $d['year_score'] ?? null,
                ];
            }
        }

        // ── Certificate records: most recent per student ───────────────────────
        $certsByStudent = [];

        if (!empty($allIds)) {
            foreach ($db->table('certificate_records')
                ->select('student_id, certificate_number, issued_at')
                ->whereIn('student_id', $allIds)
                ->where('school_year', $schoolYear)
                ->orderBy('issued_at', 'DESC')
                ->get()
                ->getResultArray() as $c) {
                $sid = (int)$c['student_id'];

                if (!isset($certsByStudent[$sid])) {
                    $certsByStudent[$sid] = $c['certificate_number'];
                }
            }
        }

        // ── Build per-class buckets and stats ──────────────────────────────────
        $studentsByClass = [];
        $statsPerClass   = [];

        foreach ($allEnrolled as $row) {
            $sid  = (int)$row['student_id'];
            $csid = (int)$row['class_section_id'];

            if (!isset($statsPerClass[$csid])) {
                $statsPerClass[$csid] = [
                    'name'  => $row['class_section_name'],
                    'total' => 0,
                    'pass'  => 0,
                    'cert'  => 0,
                ];
            }

            $statsPerClass[$csid]['total']++;

            // If no generated decision exists yet, explicitly mark pending.
            if (!isset($decisionsByStudent[$sid])) {
                $decisionsByStudent[$sid]['Decision'] = [
                    'decision'   => '',
                    'source'     => 'pending',
                    'notes'      => '',
                    'year_score' => null,
                ];
            }

            // Certificate eligibility:
            // A student is eligible only if the saved final generated decision is Pass.
            $studentDecisions = $decisionsByStudent[$sid] ?? [];
            $isPass           = !empty($studentDecisions);

            foreach ($studentDecisions as $decisionRow) {
                $decision = trim((string)($decisionRow['decision'] ?? ''));

                if (strcasecmp($decision, 'Pass') !== 0) {
                    $isPass = false;
                    break;
                }
            }

            if ($isPass) {
                $statsPerClass[$csid]['pass']++;
            }

            if (isset($certsByStudent[$sid])) {
                $statsPerClass[$csid]['cert']++;
            }

            $studentsByClass[$csid][] = $row;
        }

        // ── Determine default active tab ───────────────────────────────────────
        $firstCsid = !empty($allEnrolled) ? (int)$allEnrolled[0]['class_section_id'] : null;

        if ($selectedCsid === null && $firstCsid !== null) {
            $selectedCsid = (string)$firstCsid;
        }

        return view('admin/certificates/index', [
            'studentsByClass'    => $studentsByClass,
            'selectedClassId'    => $selectedCsid,
            'schoolYear'         => $schoolYear,
            'certDate'           => date('m/d/Y'),
            'decisionsByStudent' => $decisionsByStudent,
            'certsByStudent'     => $certsByStudent,
            'statsPerClass'      => $statsPerClass,
        ]);
    }

    public function auditLog()
    {
        $schoolYear  = $this->request->getGet('school_year') ?? $this->schoolYear;
        $records     = $this->certRecordModel->getAuditLog($schoolYear);
        $yearSummary = $this->certRecordModel->yearSummary();

        return view('admin/certificates/audit_log', [
            'records'     => $records,
            'schoolYear'  => $schoolYear,
            'yearSummary' => $yearSummary,
        ]);
    }

    public function csrfToken()
    {
        return $this->response
            ->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
            ->setHeader('Pragma', 'no-cache')
            ->setJSON([
                'csrf_token' => csrf_token(),
                'csrf_hash'  => csrf_hash(),
            ]);
    }

    // ─── Public verification page ──────────────────────────────────────────────

    public function verify(string $certNumber)
    {
        $record = \Config\Database::connect()
            ->table('certificate_records cr')
            ->select('cr.*, u.firstname AS admin_firstname, u.lastname AS admin_lastname')
            ->join('users u', 'u.id = cr.issued_by', 'left')
            ->groupStart()
                ->where('cr.verification_token', $certNumber)
                ->orWhere('cr.certificate_number', strtoupper($certNumber))
            ->groupEnd()
            ->get()
            ->getRowArray();

        return view('certificates/verify', [
            'record' => $record ?: null,
        ]);
    }

    // ─── Reprint an existing certificate ──────────────────────────────────────

    public function reprint(string $certNumber)
    {
        $record = \Config\Database::connect()
            ->table('certificate_records')
            ->where('certificate_number', strtoupper($certNumber))
            ->get()
            ->getRowArray();

        if (!$record) {
            return redirect()->to('administrator/certificates/log')
                ->with('error', 'Certificate not found: ' . $certNumber);
        }

        $certDateFormatted = '';

        if (!empty($record['cert_date'])) {
            $ts = strtotime((string)$record['cert_date']);

            if ($ts) {
                $certDateFormatted = date('m/d/Y', $ts);
            }
        }

        $student = [
            'firstname'    => (string)($record['student_name'] ?? ''),
            'lastname'     => '',
            'grade'        => (string)($record['grade'] ?? ''),
            'cert_number'  => (string)($record['certificate_number'] ?? ''),
            'verify_token' => $this->ensureVerificationTokenForRecord($record),
        ];

        // student_name is stored as "Firstname Lastname" — split for the PDF builder.
        $parts = explode(' ', trim($student['firstname']), 2);

        if (count($parts) === 2) {
            $student['firstname'] = $parts[0];
            $student['lastname']  = $parts[1];
        }

        $pdfData  = $this->buildPdf([$student], $certDateFormatted ?: date('m/d/Y'));
        $filename = 'Certificate_' . strtoupper($certNumber) . '.pdf';

        return $this->response
            ->setHeader('Content-Type', 'application/pdf')
            ->setHeader('Content-Disposition', 'inline; filename="' . $filename . '"')
            ->setBody($pdfData);
    }

    // ─── PDF generation ────────────────────────────────────────────────────────

    public function generate()
    {
        $studentIds     = $this->request->getPost('student_ids') ?? [];
        $certDate       = trim($this->request->getPost('cert_date') ?? date('m/d/Y'));
        $classSectionId = $this->request->getPost('class_section_id');
        $schoolYear     = $this->request->getPost('school_year') ?? $this->schoolYear;

        if (empty($studentIds)) {
            if ($this->request->isAJAX()) {
                return $this->response
                    ->setStatusCode(422)
                    ->setJSON([
                        'ok'         => false,
                        'error'      => 'Please select at least one student.',
                        'csrf_token' => csrf_token(),
                        'csrf_hash'  => csrf_hash(),
                    ]);
            }

            return redirect()->to('administrator/certificates')
                ->with('error', 'Please select at least one student.');
        }

        $studentIds = array_filter(array_map('intval', $studentIds));
        $certDate   = preg_replace('/[^0-9\/\-]/', '', $certDate);

        if (empty($studentIds)) {
            if ($this->request->isAJAX()) {
                return $this->response
                    ->setStatusCode(422)
                    ->setJSON([
                        'ok'         => false,
                        'error'      => 'Invalid student selection.',
                        'csrf_token' => csrf_token(),
                        'csrf_hash'  => csrf_hash(),
                    ]);
            }

            return redirect()->to('administrator/certificates')
                ->with('error', 'Invalid student selection.');
        }

        $db       = \Config\Database::connect();
        $students = [];

        foreach ($studentIds as $id) {
            $row = null;

            if ($classSectionId) {
                $row = $db->table('student_class sc')
                    ->select('s.id, s.firstname, s.lastname, cs.class_section_name AS grade')
                    ->join('students s', 's.id = sc.student_id')
                    ->join('classSection cs', 'cs.class_section_id = sc.class_section_id')
                    ->where('sc.class_section_id', (int)$classSectionId)
                    ->where('sc.school_year', $schoolYear)
                    ->where('s.id', $id)
                    ->get()
                    ->getRowArray();
            }

            if (!$row) {
                $s = $db->table('students')
                    ->select('id, firstname, lastname, registration_grade AS grade')
                    ->where('id', $id)
                    ->get()
                    ->getRowArray();

                $row = $s ?: null;
            }

            if ($row) {
                $students[] = $row;
            }
        }

        if (empty($students)) {
            if ($this->request->isAJAX()) {
                return $this->response
                    ->setStatusCode(422)
                    ->setJSON([
                        'ok'         => false,
                        'error'      => 'No valid students found.',
                        'csrf_token' => csrf_token(),
                        'csrf_hash'  => csrf_hash(),
                    ]);
            }

            return redirect()->to('administrator/certificates')
                ->with('error', 'No valid students found.');
        }

        $issuedAt = date('Y-m-d H:i:s');

        // Load existing certificates for these students this school year.
        $existingCerts = $db->table('certificate_records')
            ->select('id, student_id, certificate_number, verification_token')
            ->whereIn('student_id', array_column($students, 'id'))
            ->where('school_year', $schoolYear)
            ->get()
            ->getResultArray();

        $existingCertMap = [];

        foreach ($existingCerts as $ec) {
            $existingCertMap[(int)$ec['student_id']] = $ec;
        }

        foreach ($students as &$student) {
            $sid = (int)$student['id'];

            if (isset($existingCertMap[$sid])) {
                // Reuse existing certificate number — do not create a new record.
                $existing = $existingCertMap[$sid];

                $student['cert_number']  = (string)($existing['certificate_number'] ?? '');
                $student['verify_token'] = $this->ensureVerificationTokenForRecord($existing);
            } else {
                $certNumber  = $this->certRecordModel->nextNumber($schoolYear);
                $verifyToken = $this->certRecordModel->generateVerificationToken();

                $this->certRecordModel->insert([
                    'certificate_number' => $certNumber,
                    'verification_token' => $verifyToken,
                    'student_id'         => $sid,
                    'student_name'       => $student['firstname'] . ' ' . $student['lastname'],
                    'grade'              => $this->formatGrade($student['grade'] ?? ''),
                    'school_year'        => $schoolYear,
                    'class_section_id'   => $classSectionId ?: null,
                    'issued_at'          => $issuedAt,
                ]);

                $student['cert_number']  = $certNumber;
                $student['verify_token'] = $verifyToken;
            }
        }

        unset($student);

        $pdfData  = $this->buildPdf($students, $certDate);
        $filename = 'Certificates_' . date('Ymd_His') . '.pdf';

        return $this->response
            ->setHeader('Content-Type', 'application/pdf')
            ->setHeader('Content-Disposition', 'inline; filename="' . $filename . '"')
            ->setHeader('X-CSRF-TOKEN-NAME', csrf_token())
            ->setHeader('X-CSRF-TOKEN', csrf_hash())
            ->setBody($pdfData);
    }

    // ─── Helpers ───────────────────────────────────────────────────────────────

    private function parseCertDate(string $certDate): ?string
    {
        if (preg_match('#^(\d{2})/(\d{2})/(\d{4})$#', $certDate, $m)) {
            return $m[3] . '-' . $m[1] . '-' . $m[2];
        }

        if (preg_match('#^\d{4}-\d{2}-\d{2}$#', $certDate)) {
            return $certDate;
        }

        return null;
    }

    private function formatGrade(string $raw): string
    {
        $clean = trim($raw);
        $lower = strtolower($clean);

        // Strip section suffix: "Grade 1-A" → "Grade 1", "Grade 2-B" → "Grade 2".
        if (preg_match('/^grade\s*(\d+)/i', $clean, $m)) {
            return 'Grade ' . (int)$m[1];
        }

        if ($lower === 'youth') {
            return 'Youth';
        }

        if ($lower === 'kg' || $lower === 'kindergarten') {
            return 'Kindergarten';
        }

        // Raw number or number-section: "1", "1-A", "2-B" → keep number only.
        if (preg_match('/^(\d+)([- ][A-Za-z0-9]+)?$/', $clean, $m)) {
            return 'Grade ' . (int)$m[1];
        }

        return $clean;
    }

    private function ensureVerificationTokenForRecord(array $record): string
    {
        $token = trim((string)($record['verification_token'] ?? ''));

        if ($token !== '') {
            return $token;
        }

        $recordId = (int)($record['id'] ?? 0);

        if ($recordId <= 0) {
            return '';
        }

        $token = $this->certRecordModel->generateVerificationToken();

        $this->certRecordModel->update($recordId, [
            'verification_token' => $token,
        ]);

        return $token;
    }

    // ─── PDF rendering ─────────────────────────────────────────────────────────

    private function buildPdf(array $students, string $certDate): string
    {
        $fontDir = FCPATH . 'assets' . DIRECTORY_SEPARATOR . 'certificates' . DIRECTORY_SEPARATOR . 'fonts' . DIRECTORY_SEPARATOR;
        $imgDir  = FCPATH . 'assets' . DIRECTORY_SEPARATOR . 'certificates' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR;

        $edwardianFont = \TCPDF_FONTS::addTTFfont($fontDir . 'Edwardian Script ITC Regular.ttf', 'TrueTypeUnicode', '', 32);
        $garamondBold  = \TCPDF_FONTS::addTTFfont($fontDir . 'Garamond Bold.ttf', 'TrueTypeUnicode', '', 32);
        $ebGaramond    = \TCPDF_FONTS::addTTFfont($fontDir . 'EBGaramond-Regular.ttf', 'TrueTypeUnicode', '', 32);

        $pdf = new \TCPDF('L', 'pt', 'A4', true, 'UTF-8', false);

        $pdf->SetCreator('Al Rahma Sunday School');
        $pdf->SetTitle('Student Certificates');
        $pdf->SetMargins(0, 0, 0, true);
        $pdf->SetAutoPageBreak(false, 0);
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);
        $pdf->SetHeaderMargin(0);
        $pdf->SetFooterMargin(0);

        $W = $pdf->getPageWidth();
        $H = $pdf->getPageHeight();

        foreach ($students as $student) {
            $pdf->AddPage();

            $name        = trim((string)($student['firstname'] ?? '') . ' ' . (string)($student['lastname'] ?? ''));
            $grade       = $this->formatGrade($student['grade'] ?? '');
            $certNumber  = $student['cert_number'] ?? '';
            $verifyToken = $student['verify_token'] ?? '';

            $verifyUrl = $verifyToken !== ''
                ? site_url('verify/' . rawurlencode($verifyToken))
                : null;

            $this->drawCertificate(
                $pdf,
                $W,
                $H,
                $name,
                $grade,
                $certDate,
                $certNumber,
                $verifyUrl,
                $imgDir,
                $edwardianFont,
                $garamondBold,
                $ebGaramond
            );
        }

        return $pdf->Output('', 'S');
    }

    /**
     * Coordinate mapping: iTextSharp origin bottom-left → TCPDF origin top-left.
     * y_tcpdf ≈ H − y_iTextSharp
     */
    private function drawCertificate(
        \TCPDF $pdf,
        float $W,
        float $H,
        string $name,
        string $grade,
        string $certDate,
        string $certNumber,
        ?string $verifyUrl,
        string $imgDir,
        string $edwardianFont,
        string $garamondBold,
        string $ebGaramond
    ): void {
        // ── Images
        $pdf->Image($imgDir . 'title.png', 126, 0, 600);
        $pdf->Image($imgDir . 'background.png', 280, 176, 291, 340);
        $pdf->Image($imgDir . 'signature.png', 140, 410, 90, 80);

        // ── "Presented to:"
        $pdf->SetFont('times', 'B', 24);
        $pdf->SetTextColor(0, 0, 0);
        $pdf->SetXY(0, 151);
        $pdf->Cell($W, 24, 'Presented to:', 0, 0, 'C');

        // ── QR code — left-aligned with "Presented to:", vertically centred on that line.
        $qrSize = 42;

        if (!empty($verifyUrl)) {
            $qrX = 120;
            $qrY = 171 + (24 - $qrSize) / 2;

            $style = [
                'border'  => false,
                'padding' => 0,
                'fgcolor' => [0, 0, 0],
                'bgcolor' => false,
            ];

            $pdf->write2DBarcode($verifyUrl, 'QRCODE,L', $qrX, $qrY, $qrSize, $qrSize, $style, 'N');
        }

        // ── Student name — center based on actual string width.
        $pdf->SetFont($edwardianFont, '', 38);
        $nameX = ($W - $pdf->GetStringWidth($name)) / 2;
        $this->drawGradientText($pdf, $edwardianFont, 38, $name, $nameX, 221.5);

        // ── Line under name
        $pdf->SetFont('times', '', 20);
        $pdf->SetXY(0, 236);
        $pdf->Cell(600, 20, '___________________________________', 0, 0, 'R');

        // ── Description
        $pdf->SetFont($ebGaramond, '', 20);
        $pdf->SetXY(0, 273);
        $pdf->Cell($W, 20, 'for successfully completing the requirements of', 0, 0, 'C');

        // ── Grade
        $pdf->SetFont($garamondBold, '', 20);
        $pdf->SetXY(0, 310);
        $pdf->Cell($W, 20, $grade, 0, 0, 'C');

        // ── "at"
        $pdf->SetFont('times', '', 20);
        $pdf->SetXY(0, 343);
        $pdf->Cell($W, 20, 'at', 0, 0, 'C');

        // ── School name
        $pdf->SetFont($garamondBold, '', 20);
        $pdf->SetXY(0, 375);
        $pdf->Cell($W, 20, 'Al Rahma Sunday School', 0, 0, 'C');

        // ── Date
        $this->drawGradientText($pdf, $edwardianFont, 26, $certDate, 586, 456);

        // ── Date underline + label
        $pdf->SetFont('times', '', 20);
        $pdf->SetXY(0, 463);
        $pdf->Cell(742, 20, '_________________', 0, 0, 'R');
        $pdf->SetXY(650, 492);
        $pdf->Cell(80, 20, 'Date', 0, 0, 'C');

        // ── Signature underline + label
        $pdf->SetXY(106, 463);
        $pdf->Cell(200, 20, '_________________', 0, 0, 'L');
        $pdf->SetXY(106, 492);
        $pdf->Cell(168, 20, 'Signature', 0, 0, 'C');

        // ── Certificate number — 1.4 cm from bottom, 2.5 cm from left.
        if ($certNumber !== '') {
            $pdf->SetFont('helvetica', '', 8);
            $pdf->SetTextColor(150, 150, 150);
            $pdf->SetXY(70.86, $H - 39.68);
            $pdf->Cell(200, 12, 'Certificate No. ' . $certNumber, 0, 0, 'L');
            $pdf->SetTextColor(0, 0, 0);
        }
    }

    private function drawGradientText(
        \TCPDF $pdf,
        string $fontName,
        float $fontSize,
        string $text,
        float $x,
        float $y
    ): void {
        $pdf->SetFont($fontName, '', $fontSize);

        for ($i = 0; $i < 6; $i++) {
            $pdf->setAlpha(($i + 1) * 25 / 255);
            $pdf->SetTextColor(0, 0, 0);
            $pdf->Text($x + $i / 4, $y + $i / 4, $text);
        }

        $pdf->setAlpha(1);
        $pdf->SetTextColor(0, 0, 0);
        $pdf->Text($x, $y, $text);
    }
}