<?php

namespace App\Services\Teachers;

use App\Services\Staff\StaffTimeOffLinkService;
use App\Models\StaffAttendance;
use App\Models\TeacherClass;
use App\Models\User;
use App\Services\SemesterRangeService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;

class TeacherAbsenceService
{
    public function __construct(
        private TeacherConfigService $configService,
        private SemesterRangeService $semesterRangeService,
        private StaffTimeOffLinkService $staffTimeOffLinkService
    ) {
    }

    public function formData(int $userId): array
    {
        $context = $this->configService->context();
        $semester = (string) ($context['semester'] ?? '');
        $schoolYear = (string) ($context['school_year'] ?? '');

        $teacher = User::query()->find($userId);
        $displayName = $teacher
            ? trim(($teacher->firstname ?? '') . ' ' . ($teacher->lastname ?? ''))
            : 'Teacher';

        $existing = StaffAttendance::query()
            ->where('user_id', $userId)
            ->when($semester !== '', fn ($q) => $q->where('semester', $semester))
            ->when($schoolYear !== '', fn ($q) => $q->where('school_year', $schoolYear))
            ->orderByDesc('date')
            ->get()
            ->toArray();

        return [
            'teacher_name' => $displayName,
            'semester' => $semester,
            'school_year' => $schoolYear,
            'existing' => $existing,
            'available_dates' => $this->allowedAbsenceDates($schoolYear),
        ];
    }

    public function submit(int $userId, array $payload): array
    {
        $context = $this->configService->context();
        $semester = (string) ($context['semester'] ?? '');
        $schoolYear = (string) ($context['school_year'] ?? '');

        if ($schoolYear === '') {
            return [
                'ok' => false,
                'message' => 'Semester or school year not configured.',
                'status' => 422,
            ];
        }

        $dates = (array) ($payload['dates'] ?? []);
        $reasonType = trim((string) ($payload['reason_type'] ?? ''));
        $reasonText = trim((string) ($payload['reason'] ?? ''));

        if ($reasonText === '') {
            return [
                'ok' => false,
                'message' => 'Reason is required.',
                'status' => 422,
            ];
        }

        $reasonBase = $reasonType !== '' ? strtolower($reasonType) : '';
        $reason = $reasonBase !== '' ? ($reasonBase . ': ' . $reasonText) : $reasonText;

        $allowedSet = array_fill_keys($this->allowedAbsenceDates($schoolYear), true);
        $roleName = User::getUserRoleName($userId) ?: null;

        $saved = 0;
        $invalid = [];
        $savedDates = [];

        $dates = array_values(array_unique(array_map('strval', $dates)));

        foreach ($dates as $date) {
            $date = trim($date);
            if ($date === '') {
                continue;
            }

            try {
                $parsed = Carbon::createFromFormat('Y-m-d', $date);
                if ($parsed->format('Y-m-d') !== $date || empty($allowedSet[$date])) {
                    $invalid[] = $date;
                    continue;
                }
            } catch (\Throwable) {
                $invalid[] = $date;
                continue;
            }

            $semesterForDate = $this->semesterRangeService->getSemesterForDate($date);
            if ($semesterForDate === '') {
                $semesterForDate = $semester;
            }

            $record = StaffAttendance::upsertOne(
                userId: $userId,
                roleName: $roleName,
                date: $date,
                semester: $semesterForDate,
                schoolYear: $schoolYear,
                status: StaffAttendance::STATUS_ABSENT,
                reason: $reason,
                editorId: $userId
            );

            if ($record) {
                $saved++;
                $savedDates[] = $date;
            }
        }

        if (!empty($invalid)) {
            return [
                'ok' => false,
                'message' => 'Invalid dates: ' . implode(', ', $invalid),
                'status' => 422,
            ];
        }

        $this->sendPrincipalNotification(
            userId: $userId,
            roleName: $roleName,
            semester: $semester,
            schoolYear: $schoolYear,
            reasonType: $reasonType,
            reasonText: $reasonText,
            dates: $dates,
            savedDates: $savedDates
        );

        return [
            'ok' => true,
            'message' => $saved . ' day(s) saved as absent.',
            'saved' => $saved,
            'dates' => $savedDates,
            'status' => 200,
        ];
    }

    private function allowedAbsenceDates(string $schoolYear): array
    {
        $today = Carbon::today();
        $startYear = null;
        $endYear = null;

        if (preg_match('/^(\d{4})\D+(\d{4})$/', $schoolYear, $m)) {
            $startYear = (int) $m[1];
            $endYear = (int) $m[2];
        } else {
            $cy = (int) now()->format('Y');
            $cm = (int) now()->format('n');
            if ($cm >= 9) {
                $startYear = $cy;
                $endYear = $cy + 1;
            } else {
                $startYear = $cy - 1;
                $endYear = $cy;
            }
        }

        try {
            $start = Carbon::create($startYear, 9, 1)->startOfDay();
            $end = Carbon::create($endYear, 5, 31)->startOfDay();
        } catch (\Throwable) {
            return [];
        }

        if ($start->lt($today)) {
            $start = $today->copy();
        }

        $dates = [];
        $cursor = $start->copy();

        while ($cursor->lte($end)) {
            if ($cursor->dayOfWeek === Carbon::SUNDAY) {
                $dates[] = $cursor->format('Y-m-d');
            }
            $cursor->addDay();
        }

        return $dates;
    }

    private function sendPrincipalNotification(
        int $userId,
        ?string $roleName,
        string $semester,
        string $schoolYear,
        string $reasonType,
        string $reasonText,
        array $dates,
        array $savedDates
    ): void {
        try {
            $user = User::query()->find($userId);
            $fullName = trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')) ?: 'Teacher';
            $userEmail = $user->email ?? '';
            $role = $roleName ?: 'teacher';
            $dateList = !empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates);

            $subject = sprintf(
                'TimeOff Request: %s (%s) — %s',
                $fullName,
                ucfirst((string) $role),
                $dateList ?: 'No dates'
            );

            $submittedAt = now()->toDateTimeString();
            $assignedText = $this->formatAssignedClasses($userId, $schoolYear, $semester);

            $body = '<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.5;">'
                . '<h3 style="margin:0 0 8px;">New TimeOff Request</h3>'
                . '<p style="margin:0 0 12px;">A staff time-off request was submitted from the teacher portal.</p>'
                . '<table cellpadding="6" cellspacing="0" style="border-collapse:collapse;">'
                . '<tr><td><strong>Name</strong></td><td>' . e($fullName) . '</td></tr>'
                . '<tr><td><strong>Email</strong></td><td>' . e($userEmail) . '</td></tr>'
                . '<tr><td><strong>Role</strong></td><td>' . e((string) $role) . '</td></tr>'
                . '<tr><td><strong>Semester</strong></td><td>' . e($semester) . '</td></tr>'
                . '<tr><td><strong>School Year</strong></td><td>' . e($schoolYear) . '</td></tr>'
                . '<tr><td><strong>Reason Type</strong></td><td>' . e($reasonType ?: '-') . '</td></tr>'
                . '<tr><td><strong>Reason</strong></td><td>' . e($reasonText) . '</td></tr>'
                . '<tr><td><strong>Dates</strong></td><td>' . e($dateList ?: '-') . '</td></tr>'
                . '<tr><td><strong>Assigned Class(es)</strong></td><td>' . e($assignedText) . '</td></tr>'
                . '<tr><td><strong>Submitted At</strong></td><td>' . e($submittedAt) . '</td></tr>'
                . '</table>'
                . '</div>';

            $token = $this->staffTimeOffLinkService->createToken([
                'uid' => $userId,
                'email' => $userEmail,
                'name' => $fullName,
                'role' => $role,
                'dates' => $dateList ?: '-',
                'reason' => $reasonText,
                'reason_type' => $reasonType,
                'submitted_at' => $submittedAt,
                'origin' => 'teacher portal',
            ]);

            $notifyUrl = url('/timeoff/notify/' . rawurlencode($token));
            $body .= '<p style="margin-top:16px;">Click <a href="' . e($notifyUrl) . '">Send confirmation email to '
                . e($fullName)
                . '</a> so the staff member is notified automatically. This link expires in 14 days.</p>';

            $principalEmail = env('PRINCIPAL_EMAIL', 'principal@alrahmaisgl.org');

            Mail::html($body, function ($message) use ($principalEmail, $subject) {
                $message->to($principalEmail)
                    ->subject($subject);
            });
        } catch (\Throwable) {
            // Email failures should not block absence submissions.
        }
    }

    private function formatAssignedClasses(int $userId, string $schoolYear, string $semester): string
    {
        $assignments = TeacherClass::getClassAssignmentsByUserId($userId, $schoolYear, $semester);
        if (empty($assignments)) {
            return 'No assigned class';
        }

        $parts = [];
        foreach ($assignments as $assignment) {
            $name = (string) ($assignment['class_section_name'] ?? '');
            $role = (string) ($assignment['teacher_role'] ?? '');
            if ($name !== '') {
                $parts[] = $role !== '' ? ($name . ' (' . $role . ')') : $name;
            }
        }

        return !empty($parts) ? implode(', ', $parts) : 'No assigned class';
    }
}
