<?php

namespace App\Services\Attendance;

use App\Libraries\AttendanceAutoPublish;
use App\Models\AttendanceData;
use App\Models\AttendanceDay;
use App\Models\Student;
use App\Models\TeacherClass;
use Carbon\Carbon;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Facades\DB;
use RuntimeException;

class TeacherAttendanceSubmissionService
{
    public function __construct(
        protected AttendanceDay $attendanceDay,
        protected AttendanceData $attendanceData,
        protected TeacherClass $teacherClass,
        protected Student $student,
        protected AttendancePolicyService $attendancePolicyService,
        protected StudentAttendanceWriterService $studentAttendanceWriterService,
    ) {}

    public function submit(
        Authenticatable $user,
        array $data,
        callable $detectExternalSubmission,
        callable $currentSemester,
        callable $currentSchoolYear
    ): array {
        $rawSection = trim((string)$data['class_section_id']);

        if ($rawSection === '') {
            throw new RuntimeException('Missing or invalid class section.');
        }

        $sectionRow = DB::table('classSection')
            ->select('id', 'class_id', 'class_section_id')
            ->where('class_section_id', $rawSection)
            ->first();

        if (!$sectionRow && ctype_digit($rawSection)) {
            $sectionRow = DB::table('classSection')
                ->select('id', 'class_id', 'class_section_id')
                ->where('id', (int)$rawSection)
                ->first();
        }

        if (!$sectionRow) {
            throw new RuntimeException('Invalid class section.');
        }

        $classSectionId = (int)$sectionRow->class_section_id;
        $classId = (int)$sectionRow->class_id;
        $attendanceData = $data['attendance'] ?? [];
        $teachersData = $data['teachers'] ?? [];

        if (empty($attendanceData) || !is_array($attendanceData)) {
            throw new RuntimeException('No student attendance data provided.');
        }

        if (empty($teachersData) || !is_array($teachersData)) {
            throw new RuntimeException('No teacher attendance data provided.');
        }

        $rawDate = $data['date'] ?? now()->toDateString();
        $attendDate = Carbon::parse($rawDate)->toDateString();
        $semester = (string)$currentSemester();
        $schoolYear = (string)$currentSchoolYear();

        $dayRow = AttendanceDay::query()
            ->where('class_section_id', $classSectionId)
            ->whereDate('date', $attendDate)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->first();

        $currentUser = [
            'id' => $user->id,
            'roles' => method_exists($user, 'roles') ? $user->roles->pluck('name')->all() : [],
            'permissions' => method_exists($user, 'permissions') ? $user->permissions->pluck('name')->all() : [],
        ];

        $dayPolicyRow = $dayRow?->toArray() ?? [
            'status' => 'draft',
            'class_section_id' => $classSectionId,
            'date' => $attendDate,
            'semester' => $semester,
            'school_year' => $schoolYear,
        ];

        if (!$this->attendancePolicyService->canEditDay($dayPolicyRow, $currentUser)) {
            throw new RuntimeException('Attendance is locked for your role.');
        }

        $existingRows = AttendanceData::query()
            ->where('class_section_id', $classSectionId)
            ->whereDate('date', $attendDate)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->get();

        $existingByStudent = [];
        foreach ($existingRows as $row) {
            $existingByStudent[(int)$row->student_id] = $row->toArray();
        }

        $allowedStatuses = ['present', 'absent', 'late'];
        $assignedTeachers = $this->teacherClass->assignedForSectionTerm($classSectionId, $semester, $schoolYear);

        foreach ($assignedTeachers as $teacher) {
            $teacherId = (int)$teacher['teacher_id'];
            $status = strtolower((string)($teachersData[$teacherId]['status'] ?? ''));

            if (!in_array($status, $allowedStatuses, true)) {
                throw new RuntimeException('Please fill teacher attendance for all assigned teachers.');
            }
        }

        $isTeacher = $this->attendancePolicyService->isTeacher($currentUser['roles']);

        DB::transaction(function () use (
            $user,
            $assignedTeachers,
            $teachersData,
            $attendanceData,
            $existingByStudent,
            $classSectionId,
            $classId,
            $attendDate,
            $semester,
            $schoolYear,
            $isTeacher,
            $detectExternalSubmission
        ) {
            $dayRow = AttendanceDay::query()->firstOrCreate(
                [
                    'class_section_id' => $classSectionId,
                    'date' => $attendDate,
                    'semester' => $semester,
                    'school_year' => $schoolYear,
                ],
                [
                    'status' => 'draft',
                    'created_at' => now(),
                    'updated_at' => now(),
                ]
            );

            foreach ($assignedTeachers as $teacher) {
                $teacherId = (int)$teacher['teacher_id'];
                $position = strtolower((string)($teacher['position'] ?? 'main'));
                $position = $position === 'ta' ? 'ta' : 'main';

                DB::table('staff_attendance')->updateOrInsert(
                    [
                        'user_id' => $teacherId,
                        'date' => $attendDate,
                        'semester' => $semester,
                        'school_year' => $schoolYear,
                    ],
                    [
                        'role_name' => 'Teacher',
                        'class_section_id' => $classSectionId,
                        'position' => $position,
                        'status' => strtolower((string)($teachersData[$teacherId]['status'] ?? 'present')),
                        'reason' => ($teachersData[$teacherId]['reason'] ?? null) ?: null,
                        'updated_at' => now(),
                        'created_at' => now(),
                    ]
                );
            }

            foreach ($attendanceData as $row) {
                $studentId = (int)($row['student_id'] ?? 0);
                $status = strtolower(trim((string)($row['status'] ?? '')));
                $reason = trim((string)($row['reason'] ?? ''));
                $existing = $existingByStudent[$studentId] ?? null;

                if ($isTeacher && $existing) {
                    $lockInfo = $detectExternalSubmission($existing);
                    if (($lockInfo['locked'] ?? false) === true) {
                        continue;
                    }
                }

                $this->studentAttendanceWriterService->saveOrUpdateStudentAttendance([
                    'class_section_id' => $classSectionId,
                    'student_id' => $studentId,
                    'school_id' => $row['school_id'] ?? null,
                    'status' => $status,
                    'reason' => $reason,
                    'is_reported' => 'no',
                    'semester' => $semester,
                    'school_year' => $schoolYear,
                    'date' => $attendDate,
                    'class_id' => $classId,
                    'user_id' => $user->id,
                ]);
            }

            $autoPublishAt = AttendanceAutoPublish::secondSundayAfterEndOfDay($attendDate);

            $dayRow->update([
                'status' => 'submitted',
                'submitted_by' => $user->id,
                'submitted_at' => now(),
                'auto_publish_at' => $autoPublishAt,
                'updated_at' => now(),
            ]);
        });

        return [
            'ok' => true,
            'message' => 'Attendance submitted successfully.',
        ];
    }
}