<?php

namespace App\Http\Controllers\Api\Students;

use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Students\StudentAssignClassRequest;
use App\Http\Requests\Students\StudentAssignmentListRequest;
use App\Http\Requests\Students\StudentAutoDistributeRequest;
use App\Http\Requests\Students\StudentPromotionTotalsRequest;
use App\Http\Requests\Students\StudentRemoveClassRequest;
use App\Http\Requests\Students\StudentSetActiveRequest;
use App\Http\Requests\Students\StudentUpdateRequest;
use App\Http\Resources\Students\StudentAssignmentResource;
use App\Http\Resources\Students\StudentClassSectionResource;
use App\Http\Resources\Students\StudentRemovedResource;
use App\Http\Resources\Students\StudentScoreCardRowResource;
use App\Models\AttendanceRecord;
use App\Models\ClassSection;
use App\Models\EmergencyContact;
use App\Models\Incident;
use App\Models\SemesterScore;
use App\Models\Student;
use App\Models\StudentClass;
use App\Models\User;
use App\Services\Students\StudentAssignmentService;
use App\Services\Students\StudentAutoDistributionService;
use App\Services\Students\StudentDirectoryService;
use App\Services\Students\StudentProfileService;
use App\Services\Students\StudentScoreCardService;
use App\Services\Students\StudentStatusService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

class StudentController extends BaseApiController
{
    public function __construct(
        private StudentAssignmentService $assignmentService,
        private StudentDirectoryService $directoryService,
        private StudentStatusService $statusService,
        private StudentAutoDistributionService $autoDistributionService,
        private StudentProfileService $profileService,
        private StudentScoreCardService $scoreCardService
    ) {
        parent::__construct();
    }

    public function assignments(StudentAssignmentListRequest $request): JsonResponse
    {
        $data = $this->directoryService->assignmentData($request->validated()['school_year'] ?? null);

        return response()->json([
            'ok' => true,
            'students' => StudentAssignmentResource::collection($data['students']),
            'classes' => StudentClassSectionResource::collection($data['classes']),
            'schoolYears' => $data['schoolYears'],
            'selectedYear' => $data['selectedYear'],
            'currentYear' => $data['currentYear'],
            'isCurrentYear' => $data['isCurrentYear'],
        ]);
    }

    public function index(Request $request): JsonResponse
    {
        $data = array_merge($request->query->all(), $request->all(), $request->json()->all());
        $validator = Validator::make($data, [
            'school_year' => ['nullable', 'string', 'max:50'],
            'parent_id' => ['nullable', 'integer', 'min:1'],
            'parent_ids' => ['nullable', 'array'],
            'parent_ids.*' => ['integer', 'min:1'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();
        $parentIds = [];
        if (!empty($payload['parent_ids'])) {
            $parentIds = array_values(array_unique(array_map('intval', $payload['parent_ids'])));
        }
        if (!empty($payload['parent_id'])) {
            $parentIds[] = (int) $payload['parent_id'];
        }
        $parentIds = array_values(array_unique(array_filter($parentIds)));

        $data = $this->directoryService->listStudents($payload['school_year'] ?? null, $parentIds ?: null);

        return response()->json([
            'ok' => true,
            'students' => $data['students'],
            'school_year' => $data['schoolYear'],
            'current_year' => $data['currentYear'],
        ]);
    }

    public function show(int $studentId): JsonResponse
    {
        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        return response()->json([
            'ok' => true,
            'student' => $student->only([
                'id',
                'school_id',
                'firstname',
                'lastname',
                'dob',
                'age',
                'gender',
                'is_active',
                'registration_grade',
                'is_new',
                'photo_consent',
                'parent_id',
                'registration_date',
                'tuition_paid',
                'rfid_tag',
                'semester',
                'year_of_registration',
                'school_year',
            ]),
        ]);
    }

    public function store(Request $request): JsonResponse
    {
        $data = array_merge($request->query->all(), $request->all(), $request->json()->all());
        $validator = Validator::make($data, [
            'school_id' => ['nullable', 'string', 'max:50'],
            'firstname' => ['required', 'string', 'max:150'],
            'lastname' => ['required', 'string', 'max:150'],
            'dob' => ['nullable', 'date'],
            'age' => ['nullable', 'integer', 'min:0'],
            'gender' => ['nullable', 'string', 'max:20'],
            'is_active' => ['nullable', 'boolean'],
            'registration_grade' => ['nullable', 'string', 'max:50'],
            'is_new' => ['nullable', 'boolean'],
            'photo_consent' => ['nullable', 'boolean'],
            'parent_id' => ['nullable', 'integer', 'min:1'],
            'registration_date' => ['nullable', 'date'],
            'tuition_paid' => ['nullable', 'boolean'],
            'rfid_tag' => ['nullable', 'string', 'max:100'],
            'semester' => ['nullable', 'string', 'max:50'],
            'year_of_registration' => ['nullable', 'integer', 'min:1900'],
            'school_year' => ['required', 'string', 'max:50'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();
        $student = Student::query()->create($payload);

        if (empty($student->school_id)) {
            $student->school_id = $this->generateSchoolId($student->id);
            $student->save();
        }

        return response()->json(['ok' => true, 'student' => $student], 201);
    }

    public function assignClass(StudentAssignClassRequest $request): JsonResponse
    {
        $payload = $request->validated();
        $result = $this->assignmentService->assignClasses(
            (int) $payload['student_id'],
            $payload['class_section_id'],
            (bool) ($payload['is_event_only'] ?? false),
            (int) (auth()->id() ?? 0)
        );

        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function removeClass(StudentRemoveClassRequest $request): JsonResponse
    {
        $payload = $request->validated();
        $result = $this->assignmentService->removeClass(
            (int) $payload['student_id'],
            (int) $payload['class_section_id'],
            (int) (auth()->id() ?? 0)
        );

        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function removed(StudentAssignmentListRequest $request): JsonResponse
    {
        $data = $this->directoryService->removedStudents($request->validated()['school_year'] ?? null);

        return response()->json([
            'ok' => true,
            'active_students' => StudentRemovedResource::collection($data['active_students']),
            'removed_students' => StudentRemovedResource::collection($data['removed_students']),
            'school_year' => $data['school_year'],
        ]);
    }

    public function setActive(StudentSetActiveRequest $request): JsonResponse
    {
        $payload = $request->validated();
        $result = $this->statusService->setActive(
            (int) $payload['student_id'],
            (bool) $payload['is_active'],
            (int) (auth()->id() ?? 0)
        );

        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function autoDistribute(StudentAutoDistributeRequest $request): JsonResponse
    {
        $payload = $request->validated();
        $result = $this->autoDistributionService->autoDistribute(
            (int) ($payload['class_id'] ?? 0),
            (int) $payload['students_per_section'],
            $payload['school_year'] ?? null,
            (int) ($payload['class_section_id'] ?? 0),
            (int) (auth()->id() ?? 0)
        );

        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function promotionTotals(StudentPromotionTotalsRequest $request): JsonResponse
    {
        $data = $this->autoDistributionService->promotionTotals($request->validated()['school_year'] ?? null);

        return response()->json([
            'ok' => true,
            'year' => $data['year'],
            'rows' => $data['rows'],
        ]);
    }

    public function update(StudentUpdateRequest $request, int $studentId): JsonResponse
    {
        $result = $this->profileService->updateStudent($studentId, $request->validated());
        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function destroy(int $studentId): JsonResponse
    {
        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        $student->update(['is_active' => 0]);

        return response()->json(['ok' => true]);
    }

    public function classes(Request $request, int $studentId): JsonResponse
    {
        $validator = Validator::make($request->query->all(), [
            'school_year' => ['nullable', 'string', 'max:50'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $schoolYear = $validator->validated()['school_year'] ?? null;

        $rows = StudentClass::query()
            ->select('student_class.*', 'cs.class_section_name')
            ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'student_class.class_section_id')
            ->where('student_class.student_id', $studentId)
            ->when($schoolYear !== null && $schoolYear !== '', fn ($q) => $q->where('student_class.school_year', $schoolYear))
            ->orderBy('cs.class_section_name')
            ->get()
            ->toArray();

        return response()->json([
            'ok' => true,
            'student_id' => $studentId,
            'school_year' => $schoolYear,
            'classes' => $rows,
        ]);
    }

    public function assignClassForStudent(Request $request, int $studentId): JsonResponse
    {
        $data = array_merge($request->all(), $request->json()->all());
        $validator = Validator::make($data, [
            'student_id' => ['required', 'integer', 'min:1'],
            'class_section_id' => ['nullable', 'integer', 'min:1'],
            'class_section_ids' => ['nullable', 'array'],
            'class_section_ids.*' => ['integer', 'min:1'],
            'is_event_only' => ['nullable', 'boolean'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();
        if ((int) $payload['student_id'] !== $studentId) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => ['student_id' => ['student_id does not match the route studentId.']],
            ], 422);
        }

        $ids = $payload['class_section_ids'] ?? [];
        if (empty($ids) && isset($payload['class_section_id'])) {
            $ids = [(int) $payload['class_section_id']];
        }

        $result = $this->assignmentService->assignClasses(
            $studentId,
            $ids,
            (bool) ($payload['is_event_only'] ?? false),
            (int) (auth()->id() ?? 0)
        );

        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function removeClassForStudent(int $studentId, int $classSectionId): JsonResponse
    {
        $result = $this->assignmentService->removeClass(
            $studentId,
            $classSectionId,
            (int) (auth()->id() ?? 0)
        );

        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function promote(Request $request, int $studentId): JsonResponse
    {
        $data = array_merge($request->all(), $request->json()->all());
        $validator = Validator::make($data, [
            'class_section_id' => ['required', 'integer', 'min:1'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();
        $result = $this->assignmentService->assignClasses(
            $studentId,
            [(int) $payload['class_section_id']],
            false,
            (int) (auth()->id() ?? 0)
        );

        $status = $result['ok'] ? 200 : 422;

        return response()->json($result + ['ok' => (bool) $result['ok']], $status);
    }

    public function attendance(Request $request, int $studentId): JsonResponse
    {
        $validator = Validator::make($request->query->all(), [
            'school_year' => ['nullable', 'string', 'max:50'],
            'semester' => ['nullable', 'string', 'max:50'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();

        $rows = AttendanceRecord::query()
            ->where('student_id', $studentId)
            ->when(!empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year']))
            ->when(!empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester']))
            ->orderBy('school_year', 'desc')
            ->orderBy('semester', 'desc')
            ->get()
            ->toArray();

        return response()->json([
            'ok' => true,
            'student_id' => $studentId,
            'rows' => $rows,
        ]);
    }

    public function incidents(Request $request, int $studentId): JsonResponse
    {
        $validator = Validator::make($request->query->all(), [
            'school_year' => ['nullable', 'string', 'max:50'],
            'semester' => ['nullable', 'string', 'max:50'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();

        $rows = Incident::query()
            ->where('student_id', $studentId)
            ->when(!empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year']))
            ->when(!empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester']))
            ->orderByDesc('incident_datetime')
            ->get()
            ->toArray();

        return response()->json([
            'ok' => true,
            'student_id' => $studentId,
            'rows' => $rows,
        ]);
    }

    public function scores(Request $request, int $studentId): JsonResponse
    {
        $validator = Validator::make($request->query->all(), [
            'school_year' => ['nullable', 'string', 'max:50'],
            'semester' => ['nullable', 'string', 'max:50'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();

        $rows = SemesterScore::query()
            ->where('student_id', $studentId)
            ->when(!empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year']))
            ->when(!empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester']))
            ->orderBy('school_year', 'desc')
            ->orderBy('semester', 'desc')
            ->get()
            ->toArray();

        return response()->json([
            'ok' => true,
            'student_id' => $studentId,
            'rows' => $rows,
        ]);
    }

    public function notes(int $studentId): JsonResponse
    {
        return response()->json([
            'ok' => true,
            'student_id' => $studentId,
            'rows' => [],
        ]);
    }

    public function parents(int $studentId): JsonResponse
    {
        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        $parent = null;
        if (!empty($student->parent_id)) {
            $parent = User::query()
                ->select(['id', 'firstname', 'lastname', 'email', 'cellphone'])
                ->find((int) $student->parent_id);
        }

        return response()->json([
            'ok' => true,
            'student_id' => $studentId,
            'parent' => $parent,
        ]);
    }

    public function emergencyContacts(int $studentId): JsonResponse
    {
        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        $query = EmergencyContact::query();
        if (Schema::hasColumn('emergency_contacts', 'student_id')) {
            $query->where('student_id', $studentId);
        }
        if (!empty($student->parent_id)) {
            $query->orWhere('parent_id', $student->parent_id);
        }

        $rows = $query
            ->orderBy('emergency_contact_name')
            ->get()
            ->toArray();

        return response()->json([
            'ok' => true,
            'student_id' => $studentId,
            'rows' => $rows,
        ]);
    }

    public function addEmergencyContact(Request $request, int $studentId): JsonResponse
    {
        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        $data = array_merge($request->all(), $request->json()->all());
        $validator = Validator::make($data, [
            'name' => ['required', 'string', 'max:150'],
            'cellphone' => ['required', 'string', 'max:30'],
            'email' => ['nullable', 'email', 'max:150'],
            'relation' => ['nullable', 'string', 'max:80'],
            'semester' => ['nullable', 'string', 'max:50'],
            'school_year' => ['nullable', 'string', 'max:50'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $payload = $validator->validated();
        $create = [
            'parent_id' => $student->parent_id,
            'emergency_contact_name' => $payload['name'],
            'cellphone' => $payload['cellphone'],
            'email' => $payload['email'] ?? null,
            'relation' => $payload['relation'] ?? null,
            'semester' => $payload['semester'] ?? null,
            'school_year' => $payload['school_year'] ?? null,
        ];
        if (Schema::hasColumn('emergency_contacts', 'student_id')) {
            $create['student_id'] = $studentId;
        }

        $row = EmergencyContact::query()->create($create);

        return response()->json(['ok' => true, 'row' => $row], 201);
    }

    public function updateEmergencyContact(Request $request, int $studentId, int $contactId): JsonResponse
    {
        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        $data = array_merge($request->all(), $request->json()->all());
        $validator = Validator::make($data, [
            'name' => ['nullable', 'string', 'max:150'],
            'cellphone' => ['nullable', 'string', 'max:30'],
            'email' => ['nullable', 'email', 'max:150'],
            'relation' => ['nullable', 'string', 'max:80'],
            'semester' => ['nullable', 'string', 'max:50'],
            'school_year' => ['nullable', 'string', 'max:50'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $contact = EmergencyContact::query()->find($contactId);
        if (!$contact) {
            return response()->json(['ok' => false, 'message' => 'Contact not found.'], 404);
        }

        $payload = $validator->validated();
        $contact->update(array_filter([
            'emergency_contact_name' => $payload['name'] ?? null,
            'cellphone' => $payload['cellphone'] ?? null,
            'email' => $payload['email'] ?? null,
            'relation' => $payload['relation'] ?? null,
            'semester' => $payload['semester'] ?? null,
            'school_year' => $payload['school_year'] ?? null,
        ], static fn ($v) => $v !== null));

        return response()->json(['ok' => true, 'row' => $contact]);
    }

    public function updateRfid(Request $request, int $studentId): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'rfid_tag' => ['required', 'string', 'max:100'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        $student->update(['rfid_tag' => $validator->validated()['rfid_tag']]);

        return response()->json(['ok' => true]);
    }

    public function uploadPhoto(Request $request, int $studentId): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'photo' => ['required', 'file', 'image', 'max:5120'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $student = Student::query()->find($studentId);
        if (!$student) {
            return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
        }

        $file = $request->file('photo');
        $ext = $file->getClientOriginalExtension() ?: 'jpg';
        $path = "student-photos/{$studentId}." . strtolower($ext);
        Storage::disk('public')->putFileAs('student-photos', $file, "{$studentId}." . strtolower($ext));

        return response()->json([
            'ok' => true,
            'photo_url' => Storage::disk('public')->url($path),
        ]);
    }

    public function photo(int $studentId): JsonResponse
    {
        $base = "student-photos/{$studentId}";
        $candidates = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
        $disk = Storage::disk('public');

        foreach ($candidates as $ext) {
            $path = $base . '.' . $ext;
            if ($disk->exists($path)) {
                return response()->json([
                    'ok' => true,
                    'photo_url' => $disk->url($path),
                ]);
            }
        }

        return response()->json(['ok' => false, 'message' => 'Photo not found.'], 404);
    }

    private function generateSchoolId(int $studentId): string
    {
        $year = (int) date('y');
        return 'STU' . $year . str_pad((string) $studentId, 4, '0', STR_PAD_LEFT);
    }

    public function scoreCard(int $studentId): JsonResponse
    {
        $result = $this->scoreCardService->scoreCard($studentId);
        if (!$result['ok']) {
            return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Not found.'], 404);
        }

        return response()->json([
            'ok' => true,
            'student' => $result['student'],
            'rows' => StudentScoreCardRowResource::collection($result['rows']),
        ]);
    }
}
