<?php

namespace App\Services\Attendance;

use App\Models\Configuration;
use App\Models\StaffAttendance;
use App\Models\TeacherClass;
use App\Models\ClassSection;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\StreamedResponse;

class StaffAttendanceService
{
    public function __construct(
        protected Configuration $configuration,
        protected StaffAttendance $staffAttendance,
        protected TeacherClass $teacherClass,
        protected ClassSection $classSection,
        protected SemesterRangeService $semesterRangeService,
    ) {}

    public function monthData(?string $semester, ?string $schoolYear): array
    {
        $semester = $semester ?: (string)$this->configuration->getConfig('semester');
        $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year');

        [$rangeStart, $rangeEnd] = $this->semesterRangeService->getSchoolYearRange($schoolYear);
        $semesterNorm = $this->semesterRangeService->normalizeSemester($semester);

        if ($semesterNorm !== '') {
            $semesterRange = $this->semesterRangeService->getSemesterRange($schoolYear, $semesterNorm);
            if ($semesterRange) {
                [$rangeStart, $rangeEnd] = $semesterRange;
            }
        }

        $days = $this->semesterRangeService->buildSundayList($rangeStart, $rangeEnd);

        $assignedBySection = $semesterNorm !== ''
            ? $this->teacherClass->assignedBySectionForTerm($semesterNorm, $schoolYear)
            : $this->teacherClass->assignedBySectionForTerm($semester, $schoolYear);

        $sectionCodes = array_keys($assignedBySection);
        $labels = [];

        if (!empty($sectionCodes)) {
            $rows = $this->classSection->query()
                ->select(['class_section_id', 'class_section_name'])
                ->whereIn('class_section_id', $sectionCodes)
                ->get()
                ->toArray();

            foreach ($rows as $row) {
                $labels[(int)$row['class_section_id']] = trim((string)$row['class_section_name']) ?: 'Section #' . (int)$row['class_section_id'];
            }
        }

        $teacherIds = [];
        foreach ($assignedBySection as $rows) {
            foreach ($rows as $row) {
                $teacherIds[(int)$row['teacher_id']] = true;
            }
        }
        $teacherIds = array_keys($teacherIds);

        $statusByTeacher = [];
        if (!empty($teacherIds) && !empty($days)) {
            $query = DB::table('staff_attendance')
                ->select('user_id', 'date', 'status', 'reason')
                ->where('school_year', $schoolYear)
                ->whereIn('user_id', $teacherIds)
                ->whereIn('date', $days);

            if ($semesterNorm !== '') {
                $query->where('semester', $semesterNorm);
            }

            $rows = $query->get();

            foreach ($rows as $row) {
                $statusByTeacher[(int)$row->user_id][(string)$row->date] = [
                    'status' => strtolower((string)$row->status),
                    'reason' => $row->reason,
                ];
            }
        }

        $noSchoolRows = DB::table('calendar_events')
            ->select('date')
            ->where('school_year', $schoolYear)
            ->where('no_school', 1)
            ->where('date', '>=', $rangeStart)
            ->where('date', '<=', $rangeEnd)
            ->get();

        $noSchoolDays = [];
        foreach ($noSchoolRows as $row) {
            $noSchoolDays[] = (string)$row->date;
        }

        $sections = [];
        foreach ($sectionCodes as $code) {
            $teachersPayload = [];

            foreach (($assignedBySection[$code] ?? []) as $row) {
                $teacherId = (int)$row['teacher_id'];
                $name = trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')) ?: ('User#' . $teacherId);
                $position = strtolower((string)($row['position'] ?? 'main'));
                $position = in_array($position, ['main', 'ta'], true) ? $position : 'main';

                $cells = [];
                $totals = ['p' => 0, 'a' => 0, 'l' => 0];

                foreach ($days as $date) {
                    $cell = $statusByTeacher[$teacherId][$date] ?? null;
                    if (!$cell || empty($cell['status'])) {
                        continue;
                    }

                    $cells[$date] = $cell;

                    if ($cell['status'] === 'present') {
                        $totals['p']++;
                    } elseif ($cell['status'] === 'absent') {
                        $totals['a']++;
                    } elseif ($cell['status'] === 'late') {
                        $totals['l']++;
                    }
                }

                $teachersPayload[] = [
                    'teacher_id' => $teacherId,
                    'name' => $name,
                    'position' => $position,
                    'cells' => $cells,
                    'totals' => $totals,
                ];
            }

            $sections[] = [
                'section_code' => (int)$code,
                'label' => $labels[$code] ?? ('Section #' . (int)$code),
                'teachers' => $teachersPayload,
            ];
        }

        return [
            'filters' => [
                'semester' => $semester,
                'school_year' => $schoolYear,
                'range_start' => $rangeStart,
                'range_end' => $rangeEnd,
            ],
            'sundays' => $days,
            'noSchoolDays' => $noSchoolDays,
            'sections' => $sections,
            'admins' => [],
        ];
    }

    public function adminsAttendanceData(?string $date, ?string $semester, ?string $schoolYear): array
    {
        $date = $date ?: now()->toDateString();
        $semester = $semester ?: (string)$this->configuration->getConfig('semester');
        $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year');

        $allStaff = $this->staffAttendance->staffWithStatusForDay($date, $semester, $schoolYear, []);
        $userIds = [];

        foreach ($allStaff as $row) {
            $uid = (int)($row['user_id'] ?? 0);
            if ($uid > 0) {
                $userIds[$uid] = true;
            }
        }

        $userIds = array_keys($userIds);

        $rolesRows = DB::table('user_roles as ur')
            ->select('ur.user_id', 'r.id as role_id', 'r.name as role_name', 'r.slug as role_slug', 'r.priority')
            ->join('roles as r', 'r.id', '=', 'ur.role_id')
            ->where('r.is_active', 1)
            ->whereIn('ur.user_id', $userIds)
            ->get();

        $rolesByUser = [];
        foreach ($rolesRows as $row) {
            $rolesByUser[(int)$row->user_id][] = (array)$row;
        }

        foreach ($rolesByUser as &$list) {
            usort($list, fn($a, $b) => ((int)($a['priority'] ?? 100)) <=> ((int)($b['priority'] ?? 100)));
        }

        $excludeSlugs = ['parent', 'guest', 'teacher', 'teacher_assistant', 'ta', 'assistant_teacher'];

        $staffByUser = [];
        foreach ($allStaff as $row) {
            $uid = (int)($row['user_id'] ?? 0);
            if ($uid > 0 && !isset($staffByUser[$uid])) {
                $staffByUser[$uid] = $row;
            }
        }

        $adminsGrid = [];
        foreach ($userIds as $uid) {
            $roles = $rolesByUser[$uid] ?? [];
            $picked = null;

            foreach ($roles as $role) {
                $slug = strtolower((string)($role['role_slug'] ?? $role['role_name'] ?? ''));
                if (!in_array($slug, $excludeSlugs, true)) {
                    $picked = $role;
                    break;
                }
            }

            if (!$picked) {
                continue;
            }

            $base = $staffByUser[$uid] ?? ['user_id' => $uid];
            $base['role_name'] = $picked['role_name'] ?? 'Admin';
            $base['role_slug'] = $picked['role_slug'] ?? null;
            $adminsGrid[] = $base;
        }

        usort($adminsGrid, function ($a, $b) {
            $an = trim(($a['firstname'] ?? '') . ' ' . ($a['lastname'] ?? ''));
            $bn = trim(($b['firstname'] ?? '') . ' ' . ($b['lastname'] ?? ''));
            return strcasecmp($an, $bn);
        });

        return [
            'semester' => $semester,
            'school_year' => $schoolYear,
            'date' => $date,
            'admins_grid' => $adminsGrid,
        ];
    }

    public function saveAdmins(Authenticatable $user, array $data): array
    {
        $date = (string)$data['date'];
        $semester = (string)$data['semester'];
        $schoolYear = (string)$data['school_year'];
        $admins = (array)($data['admins'] ?? $data['staff'] ?? []);
        $allowedStatuses = ['present', 'absent', 'late'];
        $excludedRoleSlugs = ['parent', 'guest', 'teacher', 'teacher_assistant'];

        DB::transaction(function () use ($admins, $date, $semester, $schoolYear, $allowedStatuses, $excludedRoleSlugs) {
            foreach ($admins as $uid => $row) {
                $uid = (int)$uid;
                $status = strtolower(trim((string)($row['status'] ?? '')));
                $reason = trim((string)($row['reason'] ?? ''));
                $roleName = (string)($row['role_name'] ?? '');
                $roleSlug = strtolower(str_replace(['-', ' '], '_', (string)($row['role_slug'] ?? $roleName)));

                if (in_array($roleSlug, $excludedRoleSlugs, true)) {
                    continue;
                }

                if ($status === '') {
                    DB::table('staff_attendance')
                        ->where('user_id', $uid)
                        ->whereDate('date', $date)
                        ->where('semester', $semester)
                        ->where('school_year', $schoolYear)
                        ->delete();
                    continue;
                }

                if (!in_array($status, $allowedStatuses, true)) {
                    continue;
                }

                DB::table('staff_attendance')->updateOrInsert(
                    [
                        'user_id' => $uid,
                        'date' => $date,
                        'semester' => $semester,
                        'school_year' => $schoolYear,
                    ],
                    [
                        'role_name' => $roleName !== '' ? $roleName : 'Admin',
                        'status' => $status,
                        'reason' => $reason !== '' ? $reason : null,
                        'updated_at' => now(),
                        'created_at' => now(),
                    ]
                );
            }
        });

        return [
            'ok' => true,
            'message' => 'Admins attendance saved successfully.',
        ];
    }

    public function saveCell(Authenticatable $user, array $data): array
    {
        $date = (string)$data['date'];
        $status = (string)($data['status'] ?? '');
        $targetId = (int)$data['user_id'];
        $roleName = trim((string)($data['role_name'] ?? ''));
        $semester = (string)$data['semester'];
        $schoolYear = (string)$data['school_year'];
        $reason = trim((string)($data['reason'] ?? ''));

        if ($status === '') {
            DB::table('staff_attendance')
                ->where('user_id', $targetId)
                ->whereDate('date', $date)
                ->where('semester', $semester)
                ->where('school_year', $schoolYear)
                ->delete();

            return ['ok' => true];
        }

        DB::table('staff_attendance')->updateOrInsert(
            [
                'user_id' => $targetId,
                'date' => $date,
                'semester' => $semester,
                'school_year' => $schoolYear,
            ],
            [
                'role_name' => $roleName !== '' ? $roleName : 'Admin',
                'status' => $status,
                'reason' => $reason !== '' ? $reason : null,
                'updated_at' => now(),
                'created_at' => now(),
            ]
        );

        return ['ok' => true];
    }

    public function monthCsv(?string $semester, ?string $schoolYear, ?string $scope): StreamedResponse
    {
        $semester = $semester ?: (string)$this->configuration->getConfig('semester');
        $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year');
        $scope = strtolower((string)$scope);

        [$rangeStart, $rangeEnd] = $this->semesterRangeService->getSchoolYearRange($schoolYear);
        $semesterNorm = $this->semesterRangeService->normalizeSemester($semester);
        if ($semesterNorm !== '') {
            $semesterRange = $this->semesterRangeService->getSemesterRange($schoolYear, $semesterNorm);
            if ($semesterRange) {
                [$rangeStart, $rangeEnd] = $semesterRange;
            }
        }

        $daysList = $this->semesterRangeService->buildSundayList($rangeStart, $rangeEnd);

        $filename = ($scope === 'admins' ? 'admin_attendance_' : 'teacher_attendance_')
            . $semester . '_' . str_replace('/', '-', $schoolYear) . '.csv';

        return response()->streamDownload(function () use ($semester, $schoolYear, $scope, $daysList, $semesterNorm, $rangeStart, $rangeEnd) {
            $out = fopen('php://output', 'w');

            if ($scope === 'admins') {
                fputcsv($out, ['User ID', 'Date', 'Status', 'Reason']);

                $query = DB::table('staff_attendance')
                    ->select('user_id', 'date', 'status', 'reason')
                    ->where('school_year', $schoolYear)
                    ->where('date', '>=', $rangeStart)
                    ->where('date', '<=', $rangeEnd);

                if ($semesterNorm !== '') {
                    $query->where('semester', $semesterNorm);
                }

                foreach ($query->orderBy('date')->get() as $row) {
                    fputcsv($out, [
                        $row->user_id,
                        $row->date,
                        strtoupper(substr((string)$row->status, 0, 1)),
                        $row->reason,
                    ]);
                }

                fclose($out);
                return;
            }

            fputcsv($out, ['Teacher ID', 'Date', 'Status', 'Reason']);

            $query = DB::table('staff_attendance')
                ->select('user_id', 'date', 'status', 'reason')
                ->where('school_year', $schoolYear)
                ->where('date', '>=', $rangeStart)
                ->where('date', '<=', $rangeEnd);

            if ($semesterNorm !== '') {
                $query->where('semester', $semesterNorm);
            }

            foreach ($query->orderBy('date')->get() as $row) {
                fputcsv($out, [
                    $row->user_id,
                    $row->date,
                    strtoupper(substr((string)$row->status, 0, 1)),
                    $row->reason,
                ]);
            }

            fclose($out);
        }, $filename);
    }
}