<?php

namespace App\Http\Controllers;

use App\Http\Requests\StudentRequest;
use App\Models\AcademicSession;
use App\Models\Family;
use App\Models\SchoolClass;
use App\Models\Section;
use App\Models\Student;
use App\Services\StudentService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
use Illuminate\View\View;

class StudentController extends Controller implements HasMiddleware
{
    public function __construct(private StudentService $service)
    {
    }

    public static function middleware(): array
    {
        return [
            new Middleware('can:viewAny,'.Student::class, only: ['index']),
            new Middleware('can:create,'.Student::class, only: ['create', 'store']),
            new Middleware('can:view,student', only: ['show']),
            new Middleware('can:update,student', only: ['edit', 'update', 'changeStatus', 'transfer', 'promote', 'createLogin']),
            new Middleware('permission:fees.update', only: ['updateFeeHeads']),
            new Middleware('can:delete,student', only: ['destroy']),
        ];
    }

    /** Save the per-student fee overrides from the profile Fees tab. */
    public function updateFeeHeads(Request $request, Student $student): RedirectResponse
    {
        $data = $request->validate([
            'fees' => ['nullable', 'array'],
            'fees.*' => ['nullable', 'numeric', 'min:0'],
        ]);

        $this->service->updateFeeHeads($student, $data['fees'] ?? []);

        return redirect()->route('students.show', $student)->with('success', 'Fee amounts updated.');
    }

    public function index(Request $request)
    {
        $filters = $request->only(['class_id', 'section_id', 'status', 'search', 'free']);

        // PDF / Excel export of the filtered list.
        if (in_array($request->input('export'), ['pdf', 'csv'], true)) {
            $list = $this->service->listingQuery($filters)->with(['schoolClass', 'section', 'family'])->orderBy('name')->get();
            $exporter = app(\App\Support\ReportExporter::class);
            $headings = ['Adm #', 'Name', 'Class', 'Section', 'Gender', 'Father Name', 'Family', 'Mobile', 'Status'];
            $rows = $list->map(fn ($s) => [
                $s->admission_number, $s->name, $s->schoolClass?->name ?? '-', $s->section?->name ?? '-',
                ucfirst((string) $s->gender), $s->family?->father_name ?: '-', $s->family?->family_code,
                $s->family?->primary_mobile, ucfirst(str_replace('_', ' ', $s->status)),
            ])->all();

            return $request->input('export') === 'csv'
                ? $exporter->csv('students', $headings, $rows)
                : $exporter->pdf('students', 'Students List', $headings, $rows, ['Total' => $list->count()], 'landscape');
        }

        $students = $this->service->paginatedListing($filters);

        return view('students.index', [
            'students' => $students,
            'classes' => SchoolClass::active()->orderBy('level')->get(),
            'sections' => Section::active()->get(['id', 'class_id', 'name']),
            'filters' => $filters,
        ]);
    }

    public function create(Request $request): View
    {
        $student = new Student(['family_id' => $request->integer('family_id')]);

        return view('students.create', $this->formData($student));
    }

    public function store(StudentRequest $request, \App\Services\FeeService $fees): RedirectResponse
    {
        $student = $this->service->create($request->validated());

        $message = 'Student admitted successfully.';

        // Optionally issue the family's challan for the current month right away.
        if ($request->boolean('generate_challan') && auth()->user()->can('fees.create')) {
            $challan = $this->generateFirstChallan($student, $fees);
            if ($challan) {
                return redirect()->route('challans.show', $challan)
                    ->with('success', $message.' First fee challan ('.$challan->challan_number.') generated.');
            }
            $message .= ' (No fees were billable, so no challan was created.)';
        }

        return redirect()->route('students.show', $student)->with('success', $message);
    }

    /** Generate this month's family challan covering all active fee heads (if not already billed). */
    private function generateFirstChallan(Student $student, \App\Services\FeeService $fees): ?\App\Models\FeeChallan
    {
        $student->loadMissing('family');
        $family = $student->family;
        $session = $student->academic_session_id
            ? AcademicSession::find($student->academic_session_id)
            : AcademicSession::current();
        if (! $family || ! $session) {
            return null;
        }

        $month = now()->startOfMonth();
        $existing = \App\Models\FeeChallan::where('family_id', $family->id)
            ->whereDate('billing_month', $month)->first();
        if ($existing) {
            return $existing; // already billed this month — open it
        }

        $feeTypeIds = \App\Models\FeeType::where('is_active', true)->pluck('id')->all();

        return $fees->generateFamilyChallan($family, $session, $month, $feeTypeIds, now(), now()->addDays(10));
    }

    public function show(Student $student, \App\Services\ResultService $results, \App\Services\FeeService $fees): View
    {
        $student->load(['family', 'schoolClass', 'section', 'academicSession', 'documents.creator', 'feeHeads', 'transportStop', 'transportRoute']);

        // Per-head fee breakdown for the Fees tab (override vs class default).
        $feeSession = $student->academicSession ?? AcademicSession::current();
        $feeRows = \App\Models\FeeType::where('is_active', true)->orderBy('name')->get()->map(function ($ft) use ($student, $fees, $feeSession) {
            $override = $student->feeHeads->firstWhere('fee_type_id', $ft->id);

            return [
                'type' => $ft,
                'override' => $override?->amount,
                'resolved' => $feeSession ? $fees->resolveBaseAmount($student, $ft, $feeSession) : 0,
                'is_transport' => $fees->isTransportHead($ft),
            ];
        });

        $activities = \Spatie\Activitylog\Models\Activity::where('subject_type', Student::class)
            ->where('subject_id', $student->id)
            ->with('causer')->latest()->limit(50)->get();

        // Fees — the family's challans (challans are issued per family).
        $challans = \App\Models\FeeChallan::where('family_id', $student->family_id)->latest()->get();
        // The most relevant challan to collect against (latest with a balance).
        $collectChallan = $challans->whereIn('status', ['unpaid', 'partial'])->sortByDesc('id')->first();

        // Attendance — this month's records + summary.
        $attendance = \App\Models\StudentAttendance::where('student_id', $student->id)
            ->whereBetween('date', [now()->startOfMonth(), now()->endOfMonth()])
            ->orderByDesc('date')->get();
        $attendanceSummary = [
            'present' => $attendance->whereIn('status', ['present', 'late', 'half_day'])->count(),
            'absent' => $attendance->where('status', 'absent')->count(),
            'leave' => $attendance->where('status', 'leave')->count(),
            'total' => $attendance->count(),
        ];

        // Examination — results across published exams for the student's class.
        $examResults = collect();
        if ($student->class_id) {
            $examResults = \App\Models\Exam::where('branch_id', $student->branch_id)
                ->where('is_published', true)
                ->whereHas('papers', fn ($q) => $q->where('class_id', $student->class_id))
                ->latest()->get()
                ->map(fn ($exam) => ['exam' => $exam, 'result' => $results->studentResult($exam, $student)])
                ->filter(fn ($r) => $r['result']['has_marks'])->values();
        }

        // Communication — messages sent to this student's family.
        $messages = \App\Models\CommunicationLog::where('recipient_type', 'family')
            ->where('recipient_id', $student->family_id)
            ->latest()->limit(20)->get();

        return view('students.show', [
            'student' => $student,
            'siblings' => $student->siblings(),
            'activities' => $activities,
            'challans' => $challans,
            'collectChallan' => $collectChallan,
            'attendance' => $attendance,
            'attendanceSummary' => $attendanceSummary,
            'examResults' => $examResults,
            'messages' => $messages,
            'feeRows' => $feeRows,
        ]);
    }

    public function idCard(Student $student): View
    {
        $student->load(['family', 'schoolClass', 'section', 'branch']);

        return view('students.id-card', compact('student'));
    }

    public function edit(Student $student): View
    {
        return view('students.edit', $this->formData($student));
    }

    public function update(StudentRequest $request, Student $student): RedirectResponse
    {
        $this->service->update($student->id, $request->validated());

        return redirect()->route('students.show', $student)->with('success', 'Student updated successfully.');
    }

    public function destroy(Student $student): RedirectResponse
    {
        $this->service->delete($student->id);

        return redirect()->route('students.index')->with('success', 'Student removed.');
    }

    public function changeStatus(Request $request, Student $student): RedirectResponse
    {
        $data = $request->validate([
            'status' => 'required|in:'.implode(',', Student::STATUSES),
            'status_reason' => 'nullable|string|max:255',
            'status_changed_at' => 'nullable|date',
        ]);
        $this->service->changeStatus($student, $data['status'], $data['status_reason'] ?? null, $data['status_changed_at'] ?? null);

        $labels = ['withdrawn' => 'Admission withdrawn', 'left' => 'Marked as left', 'active' => 'Student rejoined / reactivated'];
        $msg = $labels[$data['status']] ?? 'Status updated to '.str_replace('_', ' ', $data['status']);

        return back()->with('success', $msg.'.');
    }

    public function transfer(Request $request, Student $student): RedirectResponse
    {
        $request->validate([
            'class_id' => 'required|exists:classes,id',
            'section_id' => 'nullable|exists:sections,id',
        ]);
        $this->service->transfer($student, $request->only('class_id', 'section_id'));

        return back()->with('success', 'Student transferred.');
    }

    public function promote(Student $student): RedirectResponse
    {
        $this->service->promote($student);

        return back()->with('success', 'Student promoted to the next class.');
    }

    /** Create (or reset) a student-portal login. */
    public function createLogin(Student $student): RedirectResponse
    {
        $this->authorize('update', $student);

        $password = \Illuminate\Support\Str::password(10, symbols: false);
        $email = strtolower($student->admission_number).'@student.school.test';

        if ($student->loginUser) {
            $student->loginUser->update(['password' => \Illuminate\Support\Facades\Hash::make($password)]);
            $user = $student->loginUser;
        } else {
            $user = \App\Models\User::create([
                'name' => $student->name,
                'email' => $email,
                'password' => \Illuminate\Support\Facades\Hash::make($password),
                'branch_id' => $student->branch_id,
                'is_active' => true,
                'email_verified_at' => now(),
            ]);
            $user->syncRoles(['Student']);
            $student->update(['user_id' => $user->id]);
        }

        return back()->with('success', "Student login ready — Email: {$user->email}  |  Password: {$password}  (save this now)");
    }

    /** Shared data for create/edit forms. */
    private function formData(Student $student): array
    {
        $feeTypes = \App\Models\FeeType::where('is_active', true)->orderBy('name')->get();

        // class_id => [ fee_type_id => amount ] default structure map (for live prefill).
        $structure = [];
        foreach (\App\Models\FeeStructure::all() as $s) {
            $structure[$s->class_id][$s->fee_type_id] = (float) $s->amount;
        }

        return [
            'student' => $student,
            'families' => Family::orderBy('family_code')->get(),
            'classes' => SchoolClass::active()->orderBy('level')->get(),
            'sections' => Section::active()->get(['id', 'class_id', 'name']),
            'sessions' => AcademicSession::orderByDesc('id')->get(),
            'transportRoutes' => \App\Models\TransportRoute::where('is_active', true)->orderBy('name')->get(),
            'transportStops' => \App\Models\TransportStop::orderBy('name')->get(['id', 'transport_route_id', 'name', 'fare']),
            'feeTypes' => $feeTypes,
            'feeStructure' => $structure,
            'feeOverrides' => $student->exists ? $student->feeHeads()->pluck('amount', 'fee_type_id')->toArray() : [],
        ];
    }
}
