<?php

namespace App\Controllers\View;

class StickersController extends PrintablesBaseController
{
    public function stickerForm()
    {
        $data['students'] = $this->studentModel->findAll();
        $data['classes']  = $this->classSectionModel->findAll();
        $data['stickers'] = $this->stickerPresets();
        return view('printables_reports/sticker_form', $data);
    }

    public function getByClass($classId)
    {
        $builder = $this->db->table('student_class sc')
            ->select('s.id, s.firstname, s.lastname, s.registration_grade, s.gender')
            ->join('students s', 's.id = sc.student_id')
            ->where('sc.class_section_id', $classId)
            ->where('sc.school_year', $this->schoolYear)
            ->orderBy('s.lastname', 'asc');

        $students = $builder->get()->getResultArray();

        return $this->response->setJSON($students);
    }

    /** Preset sticker sizes for the view (robust keys) */
    private function stickerPresets(): array
    {
        $rows = [
            ['value' => '100x24.5',  'label' => 'Xsmall - 100x24.5 mm', 'ppg' => 20],
            ['value' => '60x30',  'label' => 'Small - 60x30 mm',   'ppg' => 24],
            ['value' => '102x34', 'label' => 'Medium - 102x34 mm', 'ppg' => 14],
            ['value' => '90x45',  'label' => 'Large - 90x45 mm',   'ppg' => 10],
        ];

        // Add aliases so different views will not break (title/text/per_page)
        return array_map(static function ($r) {
            $r['title']    = $r['title']    ?? ($r['label'] ?? ($r['value'] . ' mm'));
            $r['text']     = $r['text']     ?? $r['title'];
            $r['per_page'] = $r['per_page'] ?? ($r['ppg'] ?? null);
            return $r;
        }, $rows);
    }

    /**
     * Generate name stickers as a PDF.
     *
     * Can be used for different sticker sizes/sheets by passing parameters
     * via function args (from routes) or via GET/POST form fields.
     *
     * Accepted request params (POST takes precedence over GET):
     * - class_id               : int
     * - student_id             : int
     * - print_all              : 0|1
     * - sticker_size           : string like "102x34" (accepts upper/lower case X or the unicode multiply sign)
     * - sticker_width          : number (mm)
     * - sticker_height         : number (mm)
     * - stickers_per_page      : int  (optional cap; grid still derived from size/margins)
     * - page_size              : "A4" (default) | "Letter" | any FPDF size token
     * - orientation            : "P" (default) | "L"
     * - margin_left            : number (mm)
     * - margin_top             : number (mm)
     * - margin_right           : number (mm; defaults to margin_left)
     * - margin_bottom          : number (mm; default 10)
     * - gap_x                  : number (mm; horizontal gap between stickers)
     * - gap_y                  : number (mm; vertical gap between stickers)
     * - copies[<student_id>]   : int per-student copies in batch mode
     * - single_copies          : int copies for single student mode
     */
    public function generateStickers(
        ?int $studentId = null,
        ?float $stickerWidthArg = null,
        ?float $stickerHeightArg = null,
        ?int $stickersPerPageArg = null,
        ?string $pageSizeArg = null,
        ?string $orientationArg = null,
        ?float $marginLeftArg = null,
        ?float $marginTopArg = null,
        ?float $marginRightArg = null,
        ?float $marginBottomArg = null,
        ?float $gapXArg = null,
        ?float $gapYArg = null
    ) {
        $request   = service('request');
        $method    = strtolower($request->getMethod());
        $classId   = $request->getPost('class_id') ?: $request->getGet('class_id');
        $studentId = $studentId ?? ($request->getPost('student_id') ?: $request->getGet('student_id'));

        // Safety cast
        $classId   = $classId !== null ? (int) $classId : null;
        $studentId = $studentId !== null ? (int) $studentId : null;

        // Sanity log for school year
        if (empty($this->schoolYear)) {
            log_message('error', 'generateStickers(): $this->schoolYear is empty. Load it from ConfigurationModel.');
        } else {
            log_message('debug', 'generateStickers(): schoolYear=' . $this->schoolYear);
        }

        // Load classes that actually have students in this school year
        $classes = $this->classSectionModel
            ->select('classSection.class_section_id, classSection.class_section_name')
            ->join('student_class', 'student_class.class_section_id = classSection.class_section_id', 'inner')
            ->where('student_class.school_year', $this->schoolYear)
            ->groupBy('classSection.class_section_id, classSection.class_section_name')
            ->orderBy('classSection.class_section_name', 'ASC')
            ->findAll();

        // ---------- GET: render form ----------
        if ($method === 'get') {
            $students = [];

            if (!empty($classId)) {
                $students = $this->fetchStudentsByClass((int) $classId, $this->schoolYear);
            } else {
                $students = $this->studentModel
                    ->select('students.id, students.firstname, students.lastname')
                    ->join('student_class', 'student_class.student_id = students.id', 'inner')
                    ->where('student_class.school_year', $this->schoolYear)
                    ->groupBy('students.id, students.firstname, students.lastname')
                    ->orderBy('students.firstname', 'ASC')
                    ->orderBy('students.lastname', 'ASC')
                    ->findAll(500);
            }

            // Presets (now with robust keys expected by the view)
            $presetRows = [
                ['value' => '60x30',  'label' => 'Small - 60x30 mm',   'ppg' => 24],
                ['value' => '102x34', 'label' => 'Medium - 102x34 mm', 'ppg' => 14],
                ['value' => '90x45',  'label' => 'Large - 90x45 mm',   'ppg' => 10],
            ];
            // Normalize to include 'title', 'text', and 'per_page' so views using any of those will not error.
            $stickers = array_map(static function (array $r): array {
                $r['title']    = $r['title']    ?? ($r['label'] ?? ($r['value'] . ' mm'));
                $r['text']     = $r['text']     ?? $r['title'];
                $r['per_page'] = $r['per_page'] ?? ($r['ppg'] ?? null);
                return $r;
            }, $presetRows);

            return view('printables_reports/sticker_form', [
                'classes'  => $classes,
                'students' => $students,
                'stickers' => $stickers,
            ]);
        }

        // ---------- POST: build student list ----------
        $printAll = (int) ($request->getPost('print_all') ?? $request->getGet('print_all') ?? 0) === 1;

        $students = [];
        if (!empty($studentId)) {
            $student = $this->studentModel->find((int) $studentId);
            if (!$student) {
                return 'Student not found.';
            }
            $students = [$student];
        } elseif (!empty($classId)) {
            $students = $this->fetchStudentsByClass((int) $classId, $this->schoolYear);
            if (empty($students)) {
                return 'No students found in this class.';
            }
        } elseif ($printAll) {
            $students = $this->studentModel
                ->select('students.id, students.firstname, students.lastname, sc.class_section_id, cs.class_section_name, c.id AS class_id')
                ->join('student_class sc', 'sc.student_id = students.id', 'inner')
                ->join('classSection cs', 'cs.class_section_id = sc.class_section_id', 'inner')
                ->join('classes c', 'c.id = cs.class_id', 'inner')
                ->where('sc.school_year', $this->schoolYear)
                ->groupBy('students.id, students.firstname, students.lastname, sc.class_section_id, cs.class_section_name, c.id')
                ->orderBy('c.id', 'ASC')
                ->orderBy('cs.class_section_name', 'ASC')
                ->orderBy('students.firstname', 'ASC')
                ->orderBy('students.lastname', 'ASC')
                ->findAll(5000);

            // Exclude Youth and KG
            $students = array_values(array_filter($students, static function ($s) {
                $g = trim((string) ($s['class_section_name'] ?? ''));
                return $g !== '' && !preg_match('/^(youth|kg)(?:\b|-)/i', $g);
            }));

            if (empty($students)) {
                return 'No students found for this school year.';
            }
        } else {
            return 'Please select a student or class.';
        }

        // ---- Expand list according to per-student copy counts ----
        $copiesPosted = $request->getPost('copies'); // array|null
        $singleCopies = (int) ($request->getPost('single_copies') ?? $request->getGet('single_copies') ?? 1);
        if ($singleCopies < 0) $singleCopies = 0;

        $printList = [];
        if (!empty($studentId)) {
            $qty = max(0, $singleCopies);
            for ($k = 0; $k < $qty; $k++) {
                $printList[] = $students[0];
            }
        } else {
            $hasCopies  = is_array($copiesPosted);
            $defaultQty = $hasCopies ? 0 : 1;   // if copies[] present, only print IDs provided
            foreach ($students as $stu) {
                $id  = (int) ($stu['id'] ?? 0);
                $qty = $defaultQty;
                if ($hasCopies && array_key_exists((string)$id, $copiesPosted)) {
                    $qty = (int) $copiesPosted[(string)$id];
                }
                $qty = max(0, $qty);
                for ($k = 0; $k < $qty; $k++) {
                    $printList[] = $stu;
                }
            }
        }
        if (empty($printList)) {
            return 'Nothing to print. All quantities are zero.';
        }

        // ---------- Layout parameters (request/args/defaults) ----------
        $sizeStrReq = trim((string) ($request->getPost('sticker_size') ?? $request->getGet('sticker_size') ?? ''));
        $stickerWidth  = $stickerWidthArg  ?? (float) ($request->getPost('sticker_width')  ?? $request->getGet('sticker_width')  ?? 0);
        $stickerHeight = $stickerHeightArg ?? (float) ($request->getPost('sticker_height') ?? $request->getGet('sticker_height') ?? 0);

        // parse size string if provided (supports decimals; accepts x/X/unicode multiply)
        if (($stickerWidth <= 0 || $stickerHeight <= 0) && $sizeStrReq !== '') {
            if (preg_match('/^\\s*(\\d+(?:\\.\\d+)?)\\s*[xX\\x{00D7}]\\s*(\\d+(?:\\.\\d+)?)\\s*$/u', $sizeStrReq, $m)) {
                $stickerWidth  = (float) $m[1];
                $stickerHeight = (float) $m[2];
            }
        }

        // fallbacks to controller properties or sane defaults (mm)
        if ($stickerWidth <= 0) {
            $stickerWidth  = isset($this->stickerWidth)  ? (float)$this->stickerWidth  : 102.0;
        }
        if ($stickerHeight <= 0) {
            $stickerHeight = isset($this->stickerHeight) ? (float)$this->stickerHeight : 34.0;
        }

        $stickersPerPageReq = $stickersPerPageArg ?? (int) ($request->getPost('stickers_per_page') ?? $request->getGet('stickers_per_page') ?? 0);
        $pageSize           = $pageSizeArg        ?? (string) ($request->getPost('page_size')         ?? $request->getGet('page_size')         ?? 'A4');
        $orientation        = $orientationArg     ?? (string) ($request->getPost('orientation')       ?? $request->getGet('orientation')       ?? 'P');

        $marginLeftProvided   = $marginLeftArg   !== null || $request->getPost('margin_left')   !== null || $request->getGet('margin_left')   !== null;
        $marginTopProvided    = $marginTopArg    !== null || $request->getPost('margin_top')    !== null || $request->getGet('margin_top')    !== null;
        $marginRightProvided  = $marginRightArg  !== null || $request->getPost('margin_right')  !== null || $request->getGet('margin_right')  !== null;
        $marginBottomProvided = $marginBottomArg !== null || $request->getPost('margin_bottom') !== null || $request->getGet('margin_bottom') !== null;
        $gapXProvided         = $gapXArg         !== null || $request->getPost('gap_x')         !== null || $request->getGet('gap_x')         !== null;
        $gapYProvided         = $gapYArg         !== null || $request->getPost('gap_y')         !== null || $request->getGet('gap_y')         !== null;

        $marginLeft   = $marginLeftArg   ?? (float) ($request->getPost('margin_left')   ?? $request->getGet('margin_left')   ?? ($this->marginL ?? 6));
        $marginTop    = $marginTopArg    ?? (float) ($request->getPost('margin_top')    ?? $request->getGet('margin_top')    ?? ($this->marginT ?? 6));
        $marginRight  = $marginRightArg  ?? (float) ($request->getPost('margin_right')  ?? $request->getGet('margin_right')  ?? $marginLeft);
        $marginBottom = $marginBottomArg ?? (float) ($request->getPost('margin_bottom') ?? $request->getGet('margin_bottom') ?? 10);

        $gapX = $gapXArg ?? (float) ($request->getPost('gap_x') ?? $request->getGet('gap_x') ?? ($this->gapX ?? 0));
        $gapY = $gapYArg ?? (float) ($request->getPost('gap_y') ?? $request->getGet('gap_y') ?? ($this->gapY ?? 0));

        // If using the 100x24.5 preset (small labels) and nothing custom was provided,
        // default to tight margins and zero gaps to fit 20 per page (2 columns * 10+ rows).
        $normalizedSize = strtolower(str_replace(['×', ' '], ['x', ''], $sizeStrReq));
        $isXSmallPreset = $normalizedSize === '100x24.5' || (abs($stickerWidth - 100.0) < 0.6 && abs($stickerHeight - 24.5) < 0.6);
        if ($isXSmallPreset) {
            if (!$marginLeftProvided) {
                $marginLeft = 4.0;
                if (!$marginRightProvided) {
                    $marginRight = 4.0;
                }
            }
            if (!$marginRightProvided && $marginRight < 4.0) {
                $marginRight = 4.0;
            }
            if (!$gapXProvided) {
                $gapX = 0.0;
            }
            if (!$gapYProvided) {
                $gapY = 0.0;
            }
        }

        // ---------- FPDF setup ----------
        $pdf = new \FPDF($orientation, 'mm', $pageSize);
        $pdf->SetMargins($marginLeft, $marginTop, $marginRight);
        $pdf->SetAutoPageBreak(true, $marginBottom);
        $pdf->AddPage();
        $pdf->SetFont('Arial', '', 12);

        // Printable area
        $pageW = (float) $pdf->GetPageWidth();
        $pageH = (float) $pdf->GetPageHeight();

        $usableW = max(0.0, $pageW - ($marginLeft + $marginRight));
        $usableH = max(0.0, $pageH - ($marginTop + $marginBottom));

        // Compute columns & rows from physical dimensions and gaps.
        $cols = (int) floor(($usableW + max(0.0, $gapX)) / (max(0.1, $stickerWidth)  + max(0.0, $gapX)));
        $rows = (int) floor(($usableH + max(0.0, $gapY)) / (max(0.1, $stickerHeight) + max(0.0, $gapY)));

        $cols = max(1, $cols);
        $rows = max(1, $rows);

        // Optional cap: stickers_per_page
        $capacity = $cols * $rows;
        if ($stickersPerPageReq > 0 && $stickersPerPageReq < $capacity) {
            $rows = (int) ceil($stickersPerPageReq / $cols);
            $maxRowsFit = (int) floor(($usableH + max(0.0, $gapY)) / (max(0.1, $stickerHeight) + max(0.0, $gapY)));
            $rows = max(1, min($rows, max(1, $maxRowsFit)));
            $capacity = $cols * $rows;
        }

        // Starting positions
        $xStart = $marginLeft;
        $yStart = $marginTop;
        $x = $xStart;
        $y = $yStart;

        $pt2mm = static function (float $pt): float {
            return $pt * 0.352778;
        };

        // Draw loop
        $i = 0;
        $perPage = $capacity;

        foreach ($printList as $stu) {
            if ($i > 0 && $i % $perPage === 0) {
                $pdf->AddPage();
                $x = $xStart;
                $y = $yStart;
            }

            // Background sticker rectangle (optional; fill white)
            $pdf->SetFillColor(255, 255, 255);
            $pdf->Rect($x, $y, $stickerWidth, $stickerHeight, 'F');

            // Centered name with dynamic font size
            $fontFamily = 'Arial';
            $fontStyle  = 'B';
            $maxPt      = 16;
            $minPt      = 8;
            $hPad       = 2;

            $fullName = trim(($stu['firstname'] ?? '') . ' ' . ($stu['lastname'] ?? '')) ?: 'Student';

            $bestPt = $maxPt;
            $textW  = 0.0;
            while ($bestPt >= $minPt) {
                $pdf->SetFont($fontFamily, $fontStyle, $bestPt);
                $textW = (float) $pdf->GetStringWidth($fullName);
                if ($textW <= ($stickerWidth - 2 * $hPad)) break;
                $bestPt -= 0.5;
            }
            if ($bestPt < $minPt) {
                $bestPt = $minPt;
                $pdf->SetFont($fontFamily, $fontStyle, $bestPt);
                $textW = (float) $pdf->GetStringWidth($fullName);
            }
            $textH = $pt2mm($bestPt);

            $cellX = $x + (($stickerWidth - $textW) / 2);
            $cellY = $y + (($stickerHeight - $textH) / 2);

            $pdf->SetXY($cellX, $cellY);
            $pdf->Cell($textW, $textH, $fullName, 0, 0, 'L');

            // Advance cell position within the grid
            $i++;
            $posInRow = ($i - 1) % $cols;
            if ($posInRow === ($cols - 1)) {
                $x = $xStart;
                $y += $stickerHeight + $gapY;
            } else {
                $x += $stickerWidth + $gapX;
            }
        }

        // Output
        $pdfContent = $pdf->Output('S');
        return $this->response
            ->setHeader('Content-Type', 'application/pdf')
            ->setHeader('Content-Disposition', 'inline; filename="Student_Stickers.pdf"')
            ->setHeader('Cache-Control', 'private, max-age=0, must-revalidate')
            ->setHeader('Pragma', 'public')
            ->setHeader('Content-Length', (string) strlen($pdfContent))
            ->setBody($pdfContent);
    }
}
