<?php

namespace App\Controllers\View;
use App\Controllers\BaseController;

use App\Models\CalendarModel;
use App\Models\ConfigurationModel;
use App\Models\TeacherModel;
use App\Models\UserModel;
use App\Models\ParentMeetingScheduleModel;
use App\Services\EmailService;


class SchoolCalendarController extends BaseController
{
    protected $calendarModel;
    protected $configModel;
    protected $semester;
    protected $schoolYear;
    protected $db;
    protected $eventTypes = [
        '1st Semester Scores',
        '2nd Semester Scores',
        'Break',
        'Event',
        'Final',
        'Final Exam Draft',
        'Midterm',
        'Midterm Exam Draft',        
    ];

    protected EmailService $emailService;
    protected UserModel $userModel;
    protected TeacherModel $teacherModel;
    protected ParentMeetingScheduleModel $meetingModel;

    public function __construct()
    {
        // Load the models
        $this->calendarModel = new CalendarModel();
        $this->configModel = new ConfigurationModel();
        $this->userModel = new UserModel();
        $this->teacherModel = new TeacherModel();
        $this->emailService = new EmailService();
        $this->meetingModel = new ParentMeetingScheduleModel();
        $this->db = \Config\Database::connect();
        $this->schoolYear = $this->configModel->getConfig('school_year');
        $this->semester = $this->configModel->getConfig('semester');

        // Load helpers
        helper(['form', 'url']);
    }

    public function index()
    {
        // Get school_year / semester from query string or defaults
        $syParam   = $this->request->getGet('school_year');
        $semParam  = $this->request->getGet('semester');
        $schoolYear = $syParam ?: $this->schoolYear;
        $semester   = $semParam ?: '';

        if ($semester !== '') {
            $events = $this->calendarModel->getEventsBySchoolYearAndSemester($schoolYear, $semester);
        } else {
            $events = $this->calendarModel->getEventsBySchoolYear($schoolYear);
        }

        return view('administrator/calendar_view', [
            'events'     => $events,
            'schoolYear' => $schoolYear,
            'semester'   => $semester,
        ]);
    }


    public function edit($id)
    {
        $event = $this->calendarModel->find($id);

        if (!$event) {
            return redirect()->to('/administrator/calendar_view')->with('error', 'Event not found');
        }

        return view('administrator/calendar_edit', [
            'event' => $event,
            'eventTypes' => $this->eventTypes,
        ]);
    }


    public function update($id)
    {
        // Fetch the event by ID
        $event = $this->calendarModel->find($id);

        if (!$event) {
            return redirect()->to('/administrator/calendar_view')->with('error', 'Event not found.');
        }

        if (strtolower($this->request->getMethod()) === 'post') {
            $validation = $this->validate([
                'title' => 'required|min_length[3]',
                'start' => 'required|valid_date[Y-m-d]',
            ]);

            if (!$validation) {
                return view('administrator/calendar_edit', [
                    'validation' => $this->validator,
                    'event' => $event,
                    'eventTypes' => $this->eventTypes,
                ]);
            }

            $emailTargets = $this->emailNotificationTargetsFromRequest();

            $data = [
                'title'          => $this->request->getPost('title'),
                'description'    => $this->request->getPost('description'),
                'event_type'     => $this->request->getPost('event_type') ?: null,
                'date'           => $this->request->getPost('start'),
                'notify_parent'  => $this->request->getPost('notify_parent') ? 1 : 0,
                'notify_teacher' => $this->request->getPost('notify_teacher') ? 1 : 0,
                'notify_admin'   => $this->request->getPost('notify_admin') ? 1 : 0,
                'no_school'      => $this->request->getPost('no_school') ? 1 : 0,
                'school_year'    => $this->request->getPost('school_year') ?: $this->schoolYear,
                'semester'       => $this->request->getPost('semester')    ?: $this->semester,
            ];
            if (!$this->calendarModel->supportsEventType()) {
                unset($data['event_type']);
            }

            if ($this->calendarModel->update($id, $data)) {
                $updatedEvent = $this->calendarModel->find($id);
                $emailStatus = $this->dispatchCalendarNotificationEmails($updatedEvent, $emailTargets);
                $filters = [
                    'school_year' => $data['school_year'],
                    'semester'    => $data['semester'],
                ];
                $redirect = redirect()->to($this->buildCalendarViewUrl($filters))->with('success', 'Event updated successfully');
                if ($emailFlash = $this->buildEmailFlash($emailStatus)) {
                    $redirect = $redirect
                        ->with('email_status', $emailFlash['message'])
                        ->with('email_status_class', $emailFlash['class']);
                }
                return $redirect;
            } else {
                return redirect()->back()->with('error', 'Failed to update the event.');
            }
        }

        return view('administrator/calendar_edit', [
            'event' => $event,
            'eventTypes' => $this->eventTypes,
        ]);
    }

    public function addEvent()
    {
        if (strtolower($this->request->getMethod()) === 'post') {
            $validation = $this->validate([
                'title' => 'required|min_length[3]',
                'start' => 'required|valid_date[Y-m-d]',
            ]);

            if (!$validation) {
                return view('administrator/calendar_add_event', [
                    'validation' => $this->validator,
                    'eventTypes' => $this->eventTypes,
                ]);
            }

            $emailTargets = $this->emailNotificationTargetsFromRequest();

            // Prepare data
            $data = [
                'title'          => $this->request->getPost('title'),
                'description'    => $this->request->getPost('description'),
                'event_type'     => $this->request->getPost('event_type') ?: null,
                'date'           => $this->request->getPost('start'),
                'notify_parent'  => $this->request->getPost('notify_parent') ? 1 : 0,
                'notify_teacher' => $this->request->getPost('notify_teacher') ? 1 : 0,
                'notify_admin'   => $this->request->getPost('notify_admin') ? 1 : 0,
                'no_school'      => $this->request->getPost('no_school') ? 1 : 0,
                'school_year'    => $this->request->getPost('school_year') ?: $this->schoolYear,
                'semester'       => $this->request->getPost('semester')    ?: $this->semester,
            ];
            if (!$this->calendarModel->supportsEventType()) {
                unset($data['event_type']);
            }

            if ($this->calendarModel->save($data)) {
                $insertedId = $this->calendarModel->getInsertID();
                $savedEvent = $this->calendarModel->find($insertedId);
                $emailStatus = $this->dispatchCalendarNotificationEmails($savedEvent, $emailTargets);
                log_message('info', 'Event saved successfully: ' . print_r($data, true));
                $filters = [
                    'school_year' => $data['school_year'],
                    'semester'    => $data['semester'],
                ];
                $redirect = redirect()->to($this->buildCalendarViewUrl($filters))->with('success', 'Event added successfully');
                if ($emailFlash = $this->buildEmailFlash($emailStatus)) {
                    $redirect = $redirect
                        ->with('email_status', $emailFlash['message'])
                        ->with('email_status_class', $emailFlash['class']);
                }
                return $redirect;
            } else {
                log_message('error', 'Failed to save event: ' . print_r($data, true));
                return redirect()->back()->with('error', 'Failed to save the event.');
            }
        }

        return view('administrator/calendar_add_event', [
            'eventTypes' => $this->eventTypes,
        ]);
    }

    protected function emailNotificationTargetsFromRequest(): array
    {
        return [
            'parents'  => (bool) $this->request->getPost('send_email_parent'),
            'teachers' => (bool) $this->request->getPost('send_email_teacher'),
            'admins'   => (bool) $this->request->getPost('send_email_admin'),
        ];
    }

    protected function dispatchCalendarNotificationEmails(array $event = null, array $targets = []): array
    {
        if (empty($event) || empty(array_filter($targets))) {
            return [];
        }

        $subject = $this->buildCalendarEmailSubject($event);
        $innerBody = view('emails/calendar_notification', [
            'event' => $event,
            'calendarUrl' => base_url('administrator/calendar_view'),
        ]);
        $body = view('emails/_wrap_layout', [
            'title'     => $subject,
            'subject'   => $subject,
            'body_html' => $innerBody,
        ]);

        $results = [];
        foreach ($targets as $group => $enabled) {
            if (!$enabled) {
                continue;
            }

            $emails = $this->collectEmailsForGroup($group);
            if (empty($emails)) {
                log_message('warning', "SchoolCalendarController: no recipient emails for {$group}.");
                $results[$group] = null;
                continue;
            }

            $ok = $this->emailService->sendBcc($emails, $subject, $body, 'general');
            if (!$ok) {
                log_message('warning', "SchoolCalendarController: failed to send calendar notification to {$group}.");
            }
            $results[$group] = $ok;
        }

        return $results;
    }

    protected function collectEmailsForGroup(string $group): array
    {
        switch ($group) {
            case 'parents':
                return $this->normalizeEmailAddresses($this->userModel->getParents());
            case 'teachers':
                return $this->normalizeEmailAddresses($this->teacherModel->getAllTeachers());
            case 'admins':
                return $this->normalizeEmailAddresses($this->fetchAdminRows());
            default:
                return [];
        }
    }

    protected function normalizeEmailAddresses(array $rows): array
    {
        $emails = [];
        foreach ($rows as $row) {
            $email = trim((string) ($row['email'] ?? ''));
            if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
                continue;
            }
            $emails[$email] = true;
        }
        return array_values(array_keys($emails));
    }

    protected function fetchAdminRows(): array
    {
        $excluded = ['guest', 'teacher', 'teacher_assistant', 'parent'];
        $escaped = array_map(static fn($value) => "'" . addslashes($value) . "'", $excluded);
        $excludedList = implode(', ', $escaped);

        $builder = $this->db->table('users u')
            ->select("u.id, u.firstname, u.lastname, u.email, MAX(CASE WHEN LOWER(COALESCE(r.slug, r.name)) NOT IN({$excludedList}) THEN 1 ELSE 0 END) AS is_admin", false)
            ->join('user_roles ur', 'ur.user_id = u.id')
            ->join('roles r', 'r.id = ur.role_id')
            ->where('r.is_active', 1)
            ->where('u.email IS NOT NULL')
            ->where('u.email !=', '')
            ->groupBy('u.id, u.firstname, u.lastname, u.email')
            ->having('is_admin', 1);

        return $builder->get()->getResultArray();
    }

    protected function buildCalendarEmailSubject(array $event): string
    {
        $title = trim((string) ($event['title'] ?? 'School Calendar Update'));
        $typeSegment = !empty($event['event_type']) ? ' (' . $event['event_type'] . ')' : '';
        return 'School Calendar: ' . $title . $typeSegment;
    }

    protected function buildEmailFlash(array $status): ?array
    {
        if (empty($status)) {
            return null;
        }

        $sent = [];
        $failed = [];
        $missing = [];

        foreach ($status as $group => $result) {
            if ($result === true) {
                $sent[] = $group;
            } elseif ($result === false) {
                $failed[] = $group;
            } elseif ($result === null) {
                $missing[] = $group;
            }
        }

        $parts = [];
        if (!empty($sent)) {
            $parts[] = 'Email sent to ' . $this->humanizeGroupList($sent) . '.';
        }
        if (!empty($failed)) {
            $parts[] = 'Sending failed for ' . $this->humanizeGroupList($failed) . '.';
        }
        if (!empty($missing)) {
            $parts[] = 'No recipient emails for ' . $this->humanizeGroupList($missing) . '.';
        }

        if (empty($parts)) {
            return null;
        }

        return [
            'message' => implode(' ', $parts),
            'class'   => empty($failed) && empty($missing) ? 'info' : 'warning',
        ];
    }

    protected function humanizeGroupList(array $groups): string
    {
        $groups = array_map('strval', $groups);
        if (empty($groups)) {
            return '';
        }
        if (count($groups) === 1) {
            return $groups[0];
        }
        $last = array_pop($groups);
        return implode(', ', $groups) . ' and ' . $last;
    }

    protected function buildCalendarViewUrl(array $filters = []): string
    {
        $allowed = ['school_year', 'semester'];
        $params = [];
        foreach ($allowed as $key) {
            $value = $filters[$key] ?? '';
            if ($value !== '') {
                $params[$key] = $value;
            }
        }
        $query = http_build_query($params);
        return '/administrator/calendar_view' . ($query !== '' ? ('?' . $query) : '');
    }

    public function delete($id)
    {

        // Check if the event exists
        $event = $this->calendarModel->find($id);
        if (!$event) {
            return redirect()->to('/administrator/calendar_view')->with('error', 'Event not found.');
        }

        // Delete the event
        if ($this->calendarModel->delete($id)) {
            return redirect()->to('/administrator/calendar_view')->with('success', 'Event deleted successfully');
        } else {
            return redirect()->to('/administrator/calendar_view')->with('error', 'Failed to delete the event.');
        }
    }

    public function calendarParentView()
    {
        // Transform the events into the format FullCalendar expects
        $formattedEvents = $this->calendarView('parent');

        // Pass the formatted events to the view
        return view('/parent/calendar', ['events' => $formattedEvents]);
    }

    public function calendarTeacherView()
    {
        // Transform the events into the format FullCalendar expects
        $formattedEvents = $this->calendarView('teacher');

        // Pass the formatted events to the view
        return view('/teacher/calendar', ['events' => $formattedEvents]);
    }

    public function calendarAdminView()
    {
        // Transform the events into the format FullCalendar expects
        $formattedEvents = $this->calendarView('admin');

        // Pass the formatted events to the view
        return view('administrator/calendar', ['events' => $formattedEvents]);
    }

    private function calendarView(?string $audience = null)
    {
        // Filter events by school year
        $events = $this->calendarModel->getEventsBySchoolYear($this->schoolYear);

        // Apply audience visibility rules:
        // - If no checkboxes selected (all notify_* = 0) => visible to everyone
        // - Otherwise visible only to the selected audience
        if (in_array($audience, ['parent', 'teacher', 'admin'], true)) {
            $events = array_values(array_filter($events, function ($event) use ($audience) {
                $p = (int) ($event['notify_parent']  ?? 0);
                $t = (int) ($event['notify_teacher'] ?? 0);
                $a = (int) ($event['notify_admin']   ?? 0);
                $ns = (int) ($event['no_school']      ?? 0);
                $hasAny = ($p + $t + $a) > 0;

                // If marked as no_school, it should be visible to all audiences
                if ($ns === 1) {
                    return true;
                }

                if (!$hasAny) {
                    // No audience explicitly selected: show to everyone
                    return true;
                }

                // Audience explicitly selected: restrict to matching audience
                if ($audience === 'parent')  { return $p === 1; }
                if ($audience === 'teacher') { return $t === 1; }
                if ($audience === 'admin')   { return $a === 1; }
                return true;
            }));
        }

        // Format for FullCalendar
        $aud = $audience; // capture for closure
        $formattedEvents = array_map(function ($event) use ($aud) {
            $p  = (int) ($event['notify_parent']  ?? 0);
            $t  = (int) ($event['notify_teacher'] ?? 0);
            $a  = (int) ($event['notify_admin']   ?? 0);
            $ns = (int) ($event['no_school']      ?? 0);

            $backgroundColor = null;
            // Color no-school events yellow on teacher and parent calendars
            if (in_array($aud, ['teacher', 'parent'], true) && $ns === 1) {
                $backgroundColor = '#ffc107'; // yellow
            }

            // Highlight events dedicated to staff (teachers/admins) only in green
            // Definition: visible to staff but not to parents, and not a no-school marker
            if (in_array($aud, ['teacher', 'admin'], true) && $backgroundColor === null) {
                $isStaffOnly = ($p === 0 && ($t === 1 || $a === 1) && $ns === 0);
                if ($isStaffOnly) {
                    // Use a distinct green to differentiate from the default blue
                    $backgroundColor = '#28a745';
                }
            }

            // Title-casing for "no school" phrase when marked as no_school
            $title = $event['title'] ?? 'Untitled';
            if ($ns === 1 && is_string($title)) {
                // Replace common variants with Title Case
                $title = preg_replace('/\bno\s*[- ]\s*school\b/i', 'No School', $title);
            }

            return [
                'id'          => $event['id'],
                'title'       => $title,
                'start'       => $event['date'] ?? '', // FullCalendar expects 'start'
                'description' => $event['description'] ?? '',
                'extendedProps' => [
                    'semester'        => $event['semester'] ?? '',
                    'school_year'     => $event['school_year'] ?? '',
                    'notify_parent'   => $p,
                    'notify_teacher'  => $t,
                    'notify_admin'    => $a,
                    'no_school'       => $ns,
                    'backgroundColor' => $backgroundColor,
                ]
            ];
        }, $events);

        // Append parent meeting schedules
        $meetingEvents = $this->formatMeetingEvents($audience);
        if (!empty($meetingEvents)) {
            $formattedEvents = array_values(array_merge($formattedEvents, $meetingEvents));
        }

        return $formattedEvents;
    }

    private function formatMeetingEvents(?string $audience = null): array
    {
        $aud = $audience ?? '';
        $builder = $this->meetingModel
            ->where('school_year', $this->schoolYear)
            ->groupStart()
            ->where('status', 'scheduled')
            ->orWhere('status', 'Scheduled')
            ->orWhere('status', '')
            ->orWhere('status', null)
            ->groupEnd();

        if ($aud === 'parent') {
            $parentUserId = (int) (session()->get('user_id') ?? 0);
            if ($parentUserId <= 0) {
                return [];
            }
            $studentIds = $this->getStudentIdsForParent($parentUserId);
            if (!empty($studentIds)) {
                $builder = $builder->groupStart()
                    ->where('parent_user_id', $parentUserId)
                    ->orWhereIn('student_id', $studentIds)
                    ->groupEnd();
            } else {
                $builder = $builder->where('parent_user_id', $parentUserId);
            }
        }

        $rows = $builder->orderBy('date', 'ASC')->findAll();
        if (empty($rows)) {
            // Fallback: show any meetings if school_year was missing/mismatched
            $fallback = $this->meetingModel
                ->groupStart()
                ->where('status', 'scheduled')
                ->orWhere('status', 'Scheduled')
                ->orWhere('status', '')
                ->orWhere('status', null)
                ->groupEnd();

            if ($aud === 'parent') {
                $parentUserId = (int) (session()->get('user_id') ?? 0);
                if ($parentUserId > 0) {
                    $studentIds = $this->getStudentIdsForParent($parentUserId);
                    if (!empty($studentIds)) {
                        $fallback = $fallback->groupStart()
                            ->where('parent_user_id', $parentUserId)
                            ->orWhereIn('student_id', $studentIds)
                            ->groupEnd();
                    } else {
                        $fallback = $fallback->where('parent_user_id', $parentUserId);
                    }
                }
            }

            $rows = $fallback->orderBy('date', 'ASC')->findAll();
        }
        if (empty($rows)) {
            return [];
        }

        return array_map(function ($row) use ($aud) {
            $parentName = (string)($row['parent_name'] ?? 'Parent');
            $studentName = (string)($row['student_name'] ?? 'Student');
            $time = trim((string)($row['time'] ?? ''));
            $timeLabel = $time !== '' ? (' ' . $time) : '';

            if ($aud === 'admin') {
                $title = 'Admin Meeting';
            } else {
                $title = 'Parent Meeting';
            }

            $descParts = [];
            $descParts[] = 'Student: ' . $studentName;
            $descParts[] = 'Parent: ' . $parentName;
            if (!empty($row['class_section_name'])) {
                $descParts[] = 'Section: ' . $row['class_section_name'];
            }
            $descParts[] = 'Date/Time: ' . ($row['date'] ?? '') . $timeLabel;
            if (!empty($row['notes'])) {
                $descParts[] = 'Notes: ' . $row['notes'];
            }

            $start = (string)($row['date'] ?? '');
            if ($start !== '' && $time !== '') {
                $start = $start . 'T' . $time;
            }

            return [
                'id' => 'pm-' . (int)($row['id'] ?? 0),
                'title' => $title,
                'start' => $start,
                'description' => implode("\n", $descParts),
                'extendedProps' => [
                    'semester' => $row['semester'] ?? '',
                    'school_year' => $row['school_year'] ?? '',
                    'notify_parent' => 1,
                    'notify_admin' => 1,
                    'notify_teacher' => 0,
                    'no_school' => 0,
                    'backgroundColor' => '#6f42c1',
                ],
            ];
        }, $rows);
    }

    private function getStudentIdsForParent(int $parentUserId): array
    {
        try {
            $rows = $this->db->query(
                "SELECT DISTINCT fs.student_id
                   FROM family_guardians fg
                   JOIN family_students fs ON fs.family_id = fg.family_id
                  WHERE fg.user_id = ?",
                [$parentUserId]
            )->getResultArray();
            $ids = array_map(static fn($r) => (int)($r['student_id'] ?? 0), $rows);

            // Legacy fallback: students.parent_id
            $rows2 = $this->db->query(
                "SELECT id AS student_id
                   FROM students
                  WHERE parent_id = ?",
                [$parentUserId]
            )->getResultArray();
            foreach ($rows2 as $r) {
                $ids[] = (int)($r['student_id'] ?? 0);
            }

            return array_values(array_filter($ids, static fn($id) => $id > 0));
        } catch (\Throwable $e) {
            return [];
        }
    }
    
}
