<?php

namespace App\Controllers\View;

use App\Controllers\BaseController;
use App\Models\ClassSectionModel;
use App\Models\ConfigurationModel;
use App\Models\ExamDraftModel;
use App\Models\TeacherClassModel;
use App\Models\UserModel;
use CodeIgniter\HTTP\Files\UploadedFile;
use Config\Database;

class ExamDraftController extends BaseController
{
    protected ExamDraftModel $examDraftModel;
    protected TeacherClassModel $teacherClassModel;
    protected ClassSectionModel $classSectionModel;
    protected UserModel $userModel;
    protected ConfigurationModel $configModel;

    protected string $schoolYear;
    protected string $semester;
    protected bool $hasFinalPdfColumn = false;
    protected bool $hasIsLegacyColumn = false;

    // DB enum: draft, submitted, reviewed, finalized, rejected
    // (string literals used to avoid excess constants)

    protected const TEACHER_UPLOAD_DIR = 'exams/drafts';
    protected const FINAL_UPLOAD_DIR = 'exams/finals';
    protected const MAX_UPLOAD_BYTES = 12 * 1024 * 1024;
    protected const ALLOWED_EXTENSIONS = ['doc', 'docx'];
    protected const ADMIN_ALLOWED_EXTENSIONS = ['doc', 'docx', 'pdf'];

    protected array $examTypes = [
        'Final Exam',
        'Midterm Exam',
        'Quiz',
        'Study Guide',
        'Practice Exam',
        'Other',
    ];

    public function __construct()
    {
        $this->examDraftModel = new ExamDraftModel();
        $this->teacherClassModel = new TeacherClassModel();
        $this->classSectionModel = new ClassSectionModel();
        $this->userModel = new UserModel();
        $this->configModel = new ConfigurationModel();
        $this->db = Database::connect();

        $this->schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
        $this->semester = (string) ($this->configModel->getConfig('semester') ?? '');
        $this->hasFinalPdfColumn = $this->schemaHasColumn('exam_drafts', 'final_pdf_file');
        $this->hasIsLegacyColumn = $this->schemaHasColumn('exam_drafts', 'is_legacy');

        helper(['form', 'url', 'date']);
    }

    public function teacherIndex()
    {
        $teacherId = (int) (session()->get('user_id') ?? 0);
        if ($teacherId <= 0) {
            return redirect()->to('/login');
        }

        $assignments = $this->teacherClassModel->getClassAssignmentsByUserId($teacherId, $this->schoolYear);
        $selectedClass = $this->resolveSelectedClassSection($assignments);
        if ($selectedClass > 0) {
            session()->set('class_section_id', $selectedClass);
        }

        $allDrafts = $this->examDraftModel
            ->where('teacher_id', $teacherId)
            ->orderBy('created_at', 'DESC')
            ->findAll();

        foreach ($allDrafts as &$row) {
            if (empty($row['final_pdf_file'])) {
                $pdf = $this->ensurePdfExists($row['final_file'] ?? '', pathinfo($row['final_file'] ?? '', PATHINFO_EXTENSION));
                if ($pdf !== null) {
                    $row['final_pdf_file'] = $pdf;
                }
            }
        }
        unset($row);

        $classSectionIds = array_map(static fn($a) => (int) $a['class_section_id'], $assignments);
        $legacyExams = [];
        // Guard against missing schema column in older databases
        if ($this->hasIsLegacyColumn) {
            // Keep all submissions visible; legacy ones are also surfaced in a separate tab
            $drafts = $allDrafts;

            if (!empty($classSectionIds)) {
                $legacyExams = $this->examDraftModel
                    ->select('exam_drafts.*, cs.class_section_name')
                    ->join('classSection cs', 'cs.class_section_id = exam_drafts.class_section_id', 'left')
                    ->whereIn('exam_drafts.class_section_id', $classSectionIds)
                    ->where('exam_drafts.is_legacy', 1)
                    ->where('exam_drafts.final_file IS NOT NULL', null, false)
                    ->orderBy('cs.class_section_name', 'ASC')
                    ->orderBy('exam_drafts.created_at', 'DESC')
                    ->findAll();
            }
        } else {
            // Legacy column absent, show all drafts and skip legacy tab query
            $drafts = $allDrafts;
        }

        $validation = session()->getFlashdata('validation') ?? $this->validator;

        return view('teacher/exam_drafts', [
            'assignments' => $assignments,
            'selectedClassSection' => $selectedClass,
            'drafts' => $drafts,
            'legacyExams' => $legacyExams,
            'examTypes' => $this->examTypes,
            'statusBadges' => $this->statusBadgeMap(),
            'schoolYear' => $this->schoolYear,
            'semester' => $this->semester,
            'maxUploadBytes' => self::MAX_UPLOAD_BYTES,
            'validation' => $validation,
        ]);
    }

    public function teacherStore()
    {
        $teacherId = (int) (session()->get('user_id') ?? 0);
        if ($teacherId <= 0) {
            return redirect()->to('/login');
        }

        $classSectionId = (int) ($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0);
        $examType = trim((string) $this->request->getPost('exam_type'));
        $description = trim((string) $this->request->getPost('description'));

        if ($classSectionId <= 0) {
            return redirect()->back()->withInput()->with('error', 'Select a class section before submitting.');
        }

        $assignment = $this->teacherClassModel
            ->where('teacher_id', $teacherId)
            ->where('class_section_id', $classSectionId)
            ->first();

        if (empty($assignment)) {
            return redirect()->back()->withInput()->with('error', 'You are not assigned to the selected class section.');
        }

        session()->set('class_section_id', $classSectionId);

        if ($classSectionId <= 0) {
            $classSectionId = $this->resolveSelectedClassSection($assignments);
            if ($classSectionId <= 0) {
                return redirect()->back()->withInput()->with('error', 'Select a class section before submitting.');
            }
        }

        $file = $this->request->getFile('draft_file');
        $teacherFile = null;
        $teacherFilename = null;
        if ($file && $file->isValid() && !$file->hasMoved()) {
            $stored = $this->storeUploadedFile($file, self::TEACHER_UPLOAD_DIR);
            if ($stored === null) {
                return redirect()->back()->withInput()->with('error', 'Failed to store the uploaded file.');
            }
            $teacherFile = $stored;
            $teacherFilename = $file->getClientName();
        } elseif ($file && $file->getError() !== UPLOAD_ERR_NO_FILE) {
            return redirect()->back()->withInput()->with('error', 'Upload failed. Please try again.');
        }

        $existingDraft = $this->examDraftModel
            ->where('teacher_id', $teacherId)
            ->where('class_section_id', $classSectionId)
            ->where('semester', $this->semester)
            ->where('school_year', $this->schoolYear)
            ->orderBy('version', 'DESC')
            ->first();

        $nextVersion = 1;
        $previousId = null;
        if (!empty($existingDraft)) {
            $nextVersion = ((int) ($existingDraft['version'] ?? 1)) + 1;
            $previousId = (int) ($existingDraft['id'] ?? 0) ?: null;
        }

        $title = $examType ?: 'Exam Draft';

        $payload = [
            'teacher_id' => $teacherId,
            'class_section_id' => $classSectionId,
            'semester' => $this->semester,
            'school_year' => $this->schoolYear,
            'exam_type' => $examType ?: null,
            'draft_title' => $title,
            'description' => $description === '' ? null : $description,
            'teacher_file' => $teacherFile,
            'teacher_filename' => $teacherFilename,
            'status' => 'submitted',
            'version' => $nextVersion,
            'previous_draft_id' => $previousId,
        ];

        if ($this->examDraftModel->insert($payload)) {
            return redirect()->to('/teacher/exam-drafts')->with('success', 'Exam draft submitted for review.');
        }

        return redirect()->back()->withInput()->with('error', 'Unable to save the exam draft.');
    }

    public function adminIndex()
    {
        $allDrafts = $this->examDraftModel
            ->select('exam_drafts.*, cs.class_section_name, u.firstname AS teacher_first, u.lastname AS teacher_last, a.firstname AS admin_first, a.lastname AS admin_last')
            ->join('classSection cs', 'cs.class_section_id = exam_drafts.class_section_id', 'left')
            ->join('users u', 'u.id = exam_drafts.teacher_id', 'left')
            ->join('users a', 'a.id = exam_drafts.admin_id', 'left')
            ->orderBy('exam_drafts.created_at', 'DESC')
            ->findAll();

        foreach ($allDrafts as &$row) {
            if (empty($row['final_pdf_file'])) {
                $pdf = $this->ensurePdfExists($row['final_file'] ?? '', pathinfo($row['final_file'] ?? '', PATHINFO_EXTENSION));
                if ($pdf !== null) {
                    $row['final_pdf_file'] = $pdf;
                }
            }
        }
        unset($row);

        $classSections = $this->classSectionModel
            ->select('class_section_id, class_section_name')
            ->orderBy('class_section_name', 'ASC')
            ->findAll();

        // Group legacy uploads (admin-uploaded finalized exams) by class_section for separate tab
        $legacyByClass = [];
        if ($this->hasIsLegacyColumn) {
            // Keep all submissions visible; additionally surface legacy items in a separate tab
            $drafts = $allDrafts;

            foreach ($allDrafts as $d) {
                $isLegacy = !empty($d['is_legacy']);
                if (!$isLegacy) {
                    continue;
                }
                $cid = (int)($d['class_section_id'] ?? 0);
                if (!isset($legacyByClass[$cid])) {
                    $legacyByClass[$cid] = [
                        'class_section_id' => $cid,
                        'class_section_name' => $d['class_section_name'] ?? 'Class ' . $cid,
                        'items' => [],
                    ];
                }
                $legacyByClass[$cid]['items'][] = $d;
            }
        } else {
            // Column missing: keep behavior simple and avoid legacy tab
            $drafts = $allDrafts;
        }

        return view('administrator/exam_drafts', [
            'drafts' => $drafts,
            'statusBadges' => $this->statusBadgeMap(),
            'statusOptions' => $this->statusOptions(),
            'schoolYear' => $this->schoolYear,
            'semester' => $this->semester,
            'allowedExtensions' => self::ADMIN_ALLOWED_EXTENSIONS,
            'maxUploadBytes' => self::MAX_UPLOAD_BYTES,
            'examTypes' => $this->examTypes,
            'classSections' => $classSections,
            'legacyByClass' => $legacyByClass,
        ]);
    }

    public function adminUploadLegacy()
    {
        $adminId = (int) (session()->get('user_id') ?? 0);
        if ($adminId <= 0) {
            return redirect()->to('/login');
        }

        $classSectionId = (int) ($this->request->getPost('class_section_id') ?? 0);
        $schoolYear = trim((string) ($this->request->getPost('school_year') ?? $this->schoolYear));
        $semester = trim((string) ($this->request->getPost('semester') ?? $this->semester));
        $examType = trim((string) $this->request->getPost('exam_type'));

        if ($classSectionId <= 0) {
            return redirect()->back()->withInput()->with('error', 'Select a class section.');
        }
        if ($schoolYear === '') {
            return redirect()->back()->withInput()->with('error', 'School year is required.');
        }
        if ($semester === '') {
            return redirect()->back()->withInput()->with('error', 'Semester is required.');
        }

        $file = $this->request->getFile('old_exam_file');
        if (!$file || !$file->isValid()) {
            return redirect()->back()->withInput()->with('error', 'A valid file is required.');
        }

        $stored = $this->storeUploadedFile($file, self::FINAL_UPLOAD_DIR, self::ADMIN_ALLOWED_EXTENSIONS);
        if ($stored === null) {
            return redirect()->back()->withInput()->with('error', 'File type not allowed or upload failed.');
        }

        $payload = [
            'teacher_id' => $adminId, // store under admin user since legacy uploads are admin-only
            'class_section_id' => $classSectionId,
            'semester' => ucfirst(strtolower($semester)),
            'school_year' => $schoolYear,
            'exam_type' => $examType === '' ? null : $examType,
            'draft_title' => $examType === '' ? 'Legacy Exam' : $examType,
            'description' => null,
            'final_file' => $stored,
            'final_filename' => $file->getClientName(),
            'status' => 'finalized',
            'admin_id' => $adminId,
            'reviewed_at' => utc_now(),
            'version' => 1,
        ];
        if ($this->hasIsLegacyColumn) {
            $payload['is_legacy'] = 1;
        }

        $pdfName = null;
        if (strtolower($file->getClientExtension()) === 'pdf') {
            $pdfName = $stored;
        } else {
            $pdfName = $this->convertDocToPdf(
                $this->fullUploadPath(self::FINAL_UPLOAD_DIR, $stored),
                self::FINAL_UPLOAD_DIR
            );
        }
        if ($pdfName !== null && $this->hasFinalPdfColumn) {
            $payload['final_pdf_file'] = $pdfName;
        }

        if ($this->examDraftModel->insert($payload)) {
            return redirect()->to('/administrator/exam-drafts')->with('success', 'Old exam uploaded successfully.');
        }

        return redirect()->back()->withInput()->with('error', 'Unable to save the old exam.');
    }

    public function adminReview()
    {
        $draftId = (int) ($this->request->getPost('draft_id') ?? 0);
        if ($draftId <= 0) {
            return redirect()->back()->with('error', 'Invalid submission selected.');
        }

        $draft = $this->examDraftModel->find($draftId);
        if (empty($draft)) {
            return redirect()->back()->with('error', 'Submission not found.');
        }

        $comments = trim((string) $this->request->getPost('admin_comments'));
        $statusInput = trim((string) $this->request->getPost('review_status'));
        $currentStatus = (string) ($draft['status'] ?? 'draft');
        $status = $this->normalizeStatus(
            $statusInput !== '' ? $statusInput : $currentStatus,
            $currentStatus
        );
        $status = strtolower($status);
        if (!in_array($status, $this->statusOptions(), true)) {
            $status = 'reviewed';
        }
        $finalFile = null;
        $finalFilename = null;

        $file = $this->request->getFile('final_file');
        if ($file && $file->isValid() && !$file->hasMoved()) {
            $stored = $this->storeUploadedFile($file, self::FINAL_UPLOAD_DIR);
            if ($stored === null) {
                return redirect()->back()->with('error', 'Unable to store the final draft.')->withInput();
            }
            $finalFile = $stored;
            $finalFilename = $file->getClientName();
            // Only auto-finalize if the admin explicitly chose "finalized"
            // (previously any uploaded file forced finalization)
            if ($status === 'finalized') {
                $status = 'finalized';
            }
        } elseif ($file && $file->getError() !== UPLOAD_ERR_NO_FILE) {
            return redirect()->back()->with('error', 'Final file upload failed.');
        }

        $update = [
            'status' => $status,
            'admin_comments' => $comments === '' ? null : $comments,
            'admin_id' => (int) (session()->get('user_id') ?? 0),
            'reviewed_at' => utc_now(),
        ];

        if ($finalFile !== null) {
            $update['final_file'] = $finalFile;
            $update['final_filename'] = $finalFilename;
            $pdfName = $this->ensurePdfExists($finalFile, $file ? $file->getClientExtension() : null);
            if ($pdfName !== null && $this->hasFinalPdfColumn) {
                $update['final_pdf_file'] = $pdfName;
            }
        } elseif (strtolower($status) === 'finalized' && !empty($draft['teacher_file'])) {
            // Auto-promote teacher file when admin finalizes without uploading a final
            $copied = $this->copyDraftToFinal($draft['teacher_file']);
            if ($copied !== null) {
                $update['final_file'] = $copied;
                $update['final_filename'] = $draft['teacher_filename'] ?? $draft['teacher_file'];
                $pdfName = $this->ensurePdfExists($copied, pathinfo($copied, PATHINFO_EXTENSION));
                if ($pdfName !== null && $this->hasFinalPdfColumn) {
                    $update['final_pdf_file'] = $pdfName;
                }
            }
        }
        if (strtolower($status) === 'finalized' && $this->hasIsLegacyColumn) {
            $update['is_legacy'] = 1; // finalized exams move to Previous Exams
        }

        if ($this->hasIsLegacyColumn) {
            // Keep existing legacy flag, do not set legacy automatically here
        }

        $updated = $this->examDraftModel
            ->set($update)
            ->where('id', $draftId)
            ->update();

        if ($updated) {
            return redirect()->back()->with('success', 'Review saved successfully.');
        }

        return redirect()->back()->with('error', 'Unable to save the review.');
    }

    private function resolveSelectedClassSection(array $assignments): int
    {
        $candidate = (int) ($this->request->getGet('class_section_id') ?? session()->get('class_section_id') ?? 0);
        $validIds = array_map('intval', array_column($assignments, 'class_section_id'));
        if ($candidate > 0 && in_array($candidate, $validIds, true)) {
            return $candidate;
        }
        return $validIds[0] ?? 0;
    }

    private function statusBadgeMap(): array
    {
        return [
            'draft' => [
                'label' => 'Draft',
                'class' => 'bg-secondary text-white',
            ],
            'submitted' => [
                'label' => 'Submitted',
                'class' => 'bg-warning text-dark',
            ],
            'reviewed' => [
                'label' => 'Reviewed',
                'class' => 'bg-info text-dark',
            ],
            'finalized' => [
                'label' => 'Finalized',
                'class' => 'bg-success text-white',
            ],
            'rejected' => [
                'label' => 'Rejected',
                'class' => 'bg-danger text-white',
            ],
        ];
    }

    private function statusOptions(): array
    {
        return ['draft', 'submitted', 'reviewed', 'finalized', 'rejected'];
    }

    private function normalizeStatus(string $input, string $default): string
    {
        $input = strtolower(trim($input));
        $aliases = [
            'pending'    => 'submitted', // legacy value
            'final'      => 'finalized',
            'approved'   => 'finalized', // legacy value
        ];
        if (isset($aliases[$input])) {
            $input = $aliases[$input];
        }
        return in_array($input, $this->statusOptions(), true) ? $input : $default;
    }

    private function storeUploadedFile(UploadedFile $file, string $subdir, array $allowedExtensions = self::ALLOWED_EXTENSIONS): ?string
    {
        $ext = strtolower($file->getClientExtension());
        if (!in_array($ext, $allowedExtensions, true)) {
            return null;
        }
        if ($file->getSize() > self::MAX_UPLOAD_BYTES) {
            return null;
        }

        $destination = WRITEPATH . 'uploads/' . trim($subdir, '/');
        if (!is_dir($destination)) {
            mkdir($destination, 0755, true);
        }

        $filename = $file->getRandomName();
        try {
            $file->move($destination, $filename);
            return $filename;
        } catch (\Throwable $e) {
            log_message('error', 'ExamDraftController::storeUploadedFile error: ' . $e->getMessage());
            return null;
        }
    }

    private function convertDocToPdf(string $sourcePath, string $targetSubdir): ?string
    {
        // Only attempt conversion for doc/docx
        $ext = strtolower(pathinfo($sourcePath, PATHINFO_EXTENSION));
        if (!in_array($ext, ['doc', 'docx'], true)) {
            return null;
        }

        $targetDir = WRITEPATH . 'uploads/' . trim($targetSubdir, '/');
        if (!is_dir($targetDir)) {
            mkdir($targetDir, 0755, true);
        }

        $base = pathinfo($sourcePath, PATHINFO_FILENAME);
        $targetPath = $targetDir . '/' . $base . '.pdf';

        // Attempt conversion via LibreOffice if available
        $cmd = 'soffice --headless --convert-to pdf --outdir ' . escapeshellarg($targetDir) . ' ' . escapeshellarg($sourcePath) . ' 2>/dev/null';
        @exec($cmd);

        return is_file($targetPath) ? basename($targetPath) : null;
    }

    private function schemaHasColumn(string $table, string $column): bool
    {
        try {
            $fields = $this->db->getFieldNames($table);
            return in_array($column, $fields, true);
        } catch (\Throwable $e) {
            log_message('error', "Schema check failed for {$table}.{$column}: " . $e->getMessage());
            return false;
        }
    }

    private function fullUploadPath(string $subdir, string $filename): string
    {
        return WRITEPATH . 'uploads/' . trim($subdir, '/') . '/' . $filename;
    }

    private function neighborPdfIfExists(?string $filename, string $subdir): ?string
    {
        if (empty($filename)) return null;
        $path = $this->fullUploadPath($subdir, $filename);
        $base = pathinfo($path, PATHINFO_FILENAME);
        $dir  = pathinfo($path, PATHINFO_DIRNAME);
        $pdfPath = $dir . '/' . $base . '.pdf';
        return is_file($pdfPath) ? basename($pdfPath) : null;
    }

    private function ensurePdfExists(string $finalFilename, ?string $originalExt): ?string
    {
        $pdfNeighbor = $this->neighborPdfIfExists($finalFilename, self::FINAL_UPLOAD_DIR);
        if ($pdfNeighbor !== null) {
            return $pdfNeighbor;
        }
        $ext = strtolower((string)$originalExt);
        if ($ext === 'pdf') {
            // final file itself is already pdf
            $path = $this->fullUploadPath(self::FINAL_UPLOAD_DIR, $finalFilename);
            return is_file($path) ? $finalFilename : null;
        }
        return $this->convertDocToPdf(
            $this->fullUploadPath(self::FINAL_UPLOAD_DIR, $finalFilename),
            self::FINAL_UPLOAD_DIR
        );
    }

    private function copyDraftToFinal(string $draftFilename): ?string
    {
        $source = $this->fullUploadPath(self::TEACHER_UPLOAD_DIR, $draftFilename);
        if (!is_file($source)) {
            return null;
        }
        $destinationDir = WRITEPATH . 'uploads/' . trim(self::FINAL_UPLOAD_DIR, '/');
        if (!is_dir($destinationDir)) {
            mkdir($destinationDir, 0755, true);
        }
        $ext = pathinfo($draftFilename, PATHINFO_EXTENSION);
        $destName = uniqid('final_', true) . '.' . $ext;
        $destPath = $destinationDir . '/' . $destName;
        if (!@copy($source, $destPath)) {
            return null;
        }
        return $destName;
    }
}
