<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\ActivityLog;
use App\Models\Student;
use Illuminate\Http\Request;

class StudentController extends Controller
{
    // GET /api/students — branch-scoped, searchable, filterable, paginated.
    public function index(Request $request)
    {
        $branchId = $request->header('X-Branch-Id') ?: $request->query('branch_id');

        $query = Student::with('schoolClass')->forBranch($branchId);

        if ($s = $request->query('search')) {
            $query->where(function ($q) use ($s) {
                $q->where('first_name', 'like', "%$s%")
                    ->orWhere('last_name', 'like', "%$s%")
                    ->orWhere('admission_number', 'like', "%$s%")
                    ->orWhere('roll_number', 'like', "%$s%");
            });
        }
        foreach (['class_id', 'status', 'section', 'shift', 'admission_number'] as $f) {
            if ($request->filled($f)) {
                $query->where($f, $request->query($f));
            }
        }

        $query->orderByDesc('created_at');

        // Return a plain collection when no pagination requested (frontend table
        // paginates client-side); otherwise paginate.
        if ($request->boolean('paginate')) {
            return response()->json($query->paginate($request->integer('per_page', 25)));
        }

        return response()->json(['data' => $query->limit(2000)->get()]);
    }

    public function show(Student $student)
    {
        return response()->json(['data' => $student->load('schoolClass', 'branch')]);
    }

    public function store(Request $request)
    {
        $this->validateData($request);
        $student = Student::create($this->payload($request));
        $this->linkGuardian($student);
        ActivityLog::record('create', 'students', "Added student {$student->full_name}", $student);

        return response()->json(['data' => $student->load('schoolClass')], 201);
    }

    public function update(Request $request, Student $student)
    {
        $this->validateData($request);
        $student->update($this->payload($request));
        $this->linkGuardian($student);
        ActivityLog::record('update', 'students', "Updated student {$student->full_name}", $student);

        return response()->json(['data' => $student->load('schoolClass')]);
    }

    /**
     * Keep the household consistent: if a guardian was chosen, copy its father
     * details onto the student; otherwise find-or-create a guardian from the
     * father fields and link it — so every student belongs to one parent.
     */
    private function linkGuardian(Student $student): void
    {
        if ($student->guardian_id) {
            $g = \App\Models\Guardian::find($student->guardian_id);
            if ($g) {
                $student->forceFill([
                    'father_name' => $g->name,
                    'father_mobile' => $g->phone ?: $student->father_mobile,
                    'mother_name' => $g->mother_name ?: $student->mother_name,
                ])->saveQuietly();
            }

            return;
        }

        if (! $student->father_name) {
            return;
        }
        $g = \App\Models\Guardian::firstOrCreate(
            ['phone' => $student->father_mobile ?: null, 'name' => $student->father_name],
            [
                'branch_id' => $student->branch_id, 'relation' => 'father',
                'occupation' => $student->father_occupation, 'email' => $student->father_email,
                'national_id' => $student->father_national_id, 'address' => $student->father_address,
                'mother_name' => $student->mother_name, 'mother_phone' => $student->mother_mobile,
                'status' => 'active',
            ],
        );
        $student->forceFill(['guardian_id' => $g->id])->saveQuietly();
    }

    public function destroy(Student $student)
    {
        $student->delete(); // soft delete -> Trash
        ActivityLog::record('delete', 'students', "Deleted student {$student->full_name}", $student);

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

    public function restore($id)
    {
        $student = Student::withTrashed()->findOrFail($id);
        $student->restore();
        ActivityLog::record('restore', 'students', "Restored student {$student->full_name}", $student);

        return response()->json(['data' => $student]);
    }

    /**
     * Guardians grouped from student records — one card per family (keyed by
     * father mobile, falling back to name), with all their children and each
     * child's evaluation average so a parent's whole household is visible.
     */
    public function parents(Request $request)
    {
        $branchId = $request->header('X-Branch-Id') ?: $request->query('branch_id');

        $students = Student::query()
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->where('status', 'active')
            ->with('schoolClass:id,name')
            ->get();

        // Average completed-evaluation score per student (one query).
        $evalAvgs = \Illuminate\Support\Facades\DB::table('evaluation_results')
            ->join('evaluations', 'evaluation_results.evaluation_id', '=', 'evaluations.id')
            ->where('evaluations.status', 'completed')
            ->whereNotNull('evaluation_results.marks')
            ->select('evaluation_results.student_id', \Illuminate\Support\Facades\DB::raw('AVG(evaluation_results.marks) as a'))
            ->groupBy('evaluation_results.student_id')
            ->pluck('a', 'student_id');

        // Sibling discount beneficiaries (student_id => info) under current policy.
        $discounts = \App\Support\SiblingDiscount::forAll($students);

        $families = [];
        foreach ($students as $s) {
            $key = $s->father_mobile ?: ($s->guardian_mobile ?: ($s->father_name ?: 'parent-'.$s->id));
            if (!isset($families[$key])) {
                $families[$key] = [
                    'key' => (string) $key,
                    'father_name' => $s->father_name,
                    'father_occupation' => $s->father_occupation,
                    'father_mobile' => $s->father_mobile,
                    'mother_name' => $s->mother_name,
                    'guardian_name' => $s->guardian_name,
                    'children' => [],
                ];
            }
            $avg = isset($evalAvgs[$s->id]) ? round($evalAvgs[$s->id], 1) : null;
            $families[$key]['children'][] = [
                'id' => $s->id,
                'name' => trim($s->first_name.' '.$s->last_name),
                'gender' => $s->gender,
                'class_name' => $s->schoolClass?->name,
                'admission_number' => $s->admission_number,
                'photo' => $s->photo,
                'eval_avg' => $avg,
                'stars' => $avg === null ? 0 : round(min(5, $avg / 20), 1),
                'discount_percent' => $discounts[$s->id]['percent'] ?? 0,
                'discount_reason' => $discounts[$s->id]['reason'] ?? null,
            ];
        }

        // Most children first — biggest families surface at the top.
        $rows = collect(array_values($families))
            ->map(function ($f) {
                $avgs = collect($f['children'])->pluck('eval_avg')->filter(fn ($v) => $v !== null);
                $f['child_count'] = count($f['children']);
                $f['family_avg'] = $avgs->count() ? round($avgs->avg(), 1) : null;
                $discounted = collect($f['children'])->filter(fn ($c) => ($c['discount_percent'] ?? 0) > 0);
                $f['discount_count'] = $discounted->count();
                $f['discount_reason'] = $discounted->first()['discount_reason'] ?? null;
                $f['discount_percent'] = $discounted->max('discount_percent') ?? 0;
                return $f;
            })
            ->sortByDesc('child_count')->values();

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

    /**
     * GET /students/{student}/suggestions — practice questions matched to the
     * student's most recent performance in each subject (weak → easier, strong
     * → harder), drawn from the subject question bank.
     */
    public function suggestions(Student $student)
    {
        $perPerSubject = 4;

        // Subjects the student studies (their class's assignments; fall back to all).
        $classSubjectIds = \App\Models\ClassSubject::where('class_id', $student->class_id)->pluck('subject_id');
        $subjects = \App\Models\Subject::when($classSubjectIds->isNotEmpty(), fn ($q) => $q->whereIn('id', $classSubjectIds))
            ->orderBy('sort_order')->get();

        // The student's average per subject from completed evaluations.
        $evalIds = \App\Models\Evaluation::where('status', 'completed')->pluck('id');
        $avgBySubject = \App\Models\EvaluationResult::whereIn('evaluation_id', $evalIds)
            ->where('student_id', $student->id)->whereNotNull('marks')
            ->select('subject_id', \Illuminate\Support\Facades\DB::raw('AVG(marks) as a'))->groupBy('subject_id')
            ->pluck('a', 'subject_id');

        $out = $subjects->map(function ($subject) use ($avgBySubject, $perPerSubject) {
            $avg = isset($avgBySubject[$subject->id]) ? round($avgBySubject[$subject->id], 1) : null;
            // Weak students reinforce fundamentals; strong students get a challenge.
            $target = $avg === null ? 'easy' : ($avg >= 80 ? 'hard' : ($avg >= 60 ? 'medium' : 'easy'));

            $questions = \App\Models\Question::where('subject_id', $subject->id)->where('difficulty', $target)
                ->inRandomOrder()->limit($perPerSubject)->get();
            if ($questions->count() < $perPerSubject) {
                $questions = \App\Models\Question::where('subject_id', $subject->id)
                    ->inRandomOrder()->limit($perPerSubject)->get();
            }

            return [
                'subject_id' => $subject->id,
                'subject' => $subject->name,
                'subject_fa' => $subject->name_fa,
                'avg' => $avg,
                'level' => $target,
                'reason' => $avg === null ? 'no_data' : ($avg >= 80 ? 'challenge' : ($avg >= 60 ? 'practice' : 'reinforce')),
                'questions' => $questions->map(fn ($q) => [
                    'id' => $q->id, 'topic' => $q->topic, 'difficulty' => $q->difficulty,
                    'text' => $q->text, 'options' => $q->options, 'marks' => $q->marks,
                ]),
            ];
        })->filter(fn ($s) => count($s['questions']) > 0)->values();

        return response()->json(['data' => $out]);
    }

    /**
     * GET /students/{student}/monthly-report?month_shamsi= — a printable monthly
     * report: the month's evaluation marks (+ a weekly rank breakdown), attendance
     * for the current month, and fee status.
     */
    public function monthlyReport(Request $request, Student $student)
    {
        $student->load('schoolClass:id,name,name_fa');
        $weeks = (int) \App\Support\SystemConfig::get('weeks_per_month', 4);

        // Evaluation — the chosen (or latest completed) month for this class.
        $evalQuery = \App\Models\Evaluation::where('class_id', $student->class_id)->where('status', 'completed');
        $month = (int) ($request->query('month_shamsi') ?: 0);
        $eval = $month
            ? (clone $evalQuery)->where('month_shamsi', $month)->first()
            : (clone $evalQuery)->orderByDesc('month_shamsi')->first();

        $subjects = [];
        $evalAvg = null;
        $note = null;
        $classRank = null;
        $classSize = null;
        if ($eval) {
            $marks = \App\Models\EvaluationResult::where('evaluation_id', $eval->id)->get();
            $subjectNames = \App\Models\Subject::whereIn('id', $marks->pluck('subject_id')->unique())->get()->keyBy('id');
            $mine = $marks->where('student_id', $student->id);
            $subjects = $mine->map(fn ($r) => [
                'subject' => $subjectNames[$r->subject_id]->name ?? null,
                'subject_fa' => $subjectNames[$r->subject_id]->name_fa ?? null,
                'marks' => $r->marks,
            ])->values();
            $vals = $mine->pluck('marks')->filter(fn ($v) => $v !== null);
            $evalAvg = $vals->count() ? round($vals->avg(), 1) : null;

            // Class ranking (for the "week N" placement).
            $classAvgs = $marks->groupBy('student_id')->map(fn ($g) => $g->pluck('marks')->filter()->avg())
                ->filter()->sortDesc()->values();
            $allIds = $marks->groupBy('student_id')->map(fn ($g) => $g->pluck('marks')->filter()->avg())->filter()->sortDesc()->keys();
            $classSize = $allIds->count();
            $pos = $allIds->search($student->id);
            $classRank = $pos === false ? null : $pos + 1;

            $note = \App\Models\EvaluationStudentNote::where('evaluation_id', $eval->id)->where('student_id', $student->id)->value('note');
        }

        // Attendance for the current gregorian month.
        $from = now()->startOfMonth()->toDateString();
        $to = now()->endOfMonth()->toDateString();
        $att = \App\Models\Attendance::where('person_type', 'student')->where('person_id', $student->id)
            ->whereBetween('date', [$from, $to])->get();
        $present = $att->whereIn('status', ['present', 'late', 'activity'])->count();
        $absent = $att->where('status', 'absent')->count();
        $late = $att->where('status', 'late')->count();

        // Fees.
        $fees = \App\Models\StudentFee::where('student_id', $student->id)->get();

        return response()->json([
            'student' => [
                'id' => $student->id, 'name' => $student->full_name, 'photo' => $student->photo,
                'gender' => $student->gender, 'roll_number' => $student->roll_number,
                'admission_number' => $student->admission_number, 'father_name' => $student->father_name,
                'class_name' => $student->schoolClass?->name,
            ],
            'month' => $eval ? ['shamsi' => $eval->month_shamsi, 'name_fa' => $eval->month_name_fa, 'year' => $eval->year_shamsi] : null,
            'weeks' => $weeks,
            'weekly_rank' => $classRank && $classRank <= $weeks ? $classRank : null,
            'evaluation' => [
                'subjects' => $subjects, 'avg' => $evalAvg, 'grade' => $this->grade($evalAvg),
                'rank' => $classRank, 'class_size' => $classSize, 'note' => $note,
            ],
            'attendance' => [
                'present' => $present, 'absent' => $absent, 'late' => $late, 'total' => $att->count(),
                'pct' => $att->count() ? round($present / $att->count() * 100) : null,
            ],
            'fees' => [
                'billed' => round($fees->sum(fn ($f) => $f->amount - $f->discount), 2),
                'paid' => round($fees->sum('paid'), 2),
                'balance' => round($fees->sum(fn ($f) => $f->balance), 2),
            ],
        ]);
    }

    private function grade(?float $avg): ?string
    {
        if ($avg === null) {
            return null;
        }
        if ($avg >= 90) return 'A+';
        if ($avg >= 80) return 'A';
        if ($avg >= 70) return 'B';
        if ($avg >= 60) return 'C';
        if ($avg >= 50) return 'D';

        return 'F';
    }

    // GET /students/{student}/sibling-discount — this student's family discount, if any.
    public function siblingDiscount(Student $student)
    {
        return response()->json([
            'data' => \App\Support\SiblingDiscount::forStudent($student),
            'policy' => \App\Support\SiblingDiscount::policy(),
        ]);
    }

    /* ── Student documents (Tazkira / Passport / پارچه …) ─────────── */
    public function documents(Student $student)
    {
        $docs = \App\Models\StudentDocument::where('student_id', $student->id)
            ->orderByDesc('id')->get(['id', 'doc_type', 'title', 'mime', 'size', 'content', 'created_at']);

        return response()->json(['data' => $docs]);
    }

    public function storeDocument(Request $request, Student $student)
    {
        $data = $request->validate([
            'doc_type' => ['required', 'string'],
            'title' => ['required', 'string'],
            'mime' => ['nullable', 'string'],
            'size' => ['nullable', 'integer'],
            'content' => ['required', 'string'], // data: URI
        ]);
        $data['student_id'] = $student->id;
        $doc = \App\Models\StudentDocument::create($data);
        ActivityLog::record('create', 'students', "Uploaded {$data['doc_type']} for {$student->full_name}", $student);

        return response()->json(['data' => $doc], 201);
    }

    public function destroyDocument(Student $student, $docId)
    {
        \App\Models\StudentDocument::where('student_id', $student->id)->where('id', $docId)->delete();

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

    private function validateData(Request $request): void
    {
        $request->validate([
            'first_name' => ['required', 'string', 'max:255'],
            'last_name' => ['nullable', 'string', 'max:255'],
            'middle_name' => ['nullable', 'string', 'max:255'],
            'gender' => ['nullable', 'in:male,female,other'],
            'branch_id' => ['nullable', 'exists:branches,id'],
            'class_id' => ['nullable', 'exists:school_classes,id'],
            'email' => ['nullable', 'email'],
            // Father is mandatory — it identifies the household (sibling discount).
            'father_name' => ['required', 'string', 'max:255'],
            'guardian_id' => ['nullable', 'exists:guardians,id'],
        ]);
    }

    // Keep only real student columns from the request (the model carries 50+
    // fields; this whitelists by the actual table schema).
    private function payload(Request $request): array
    {
        $columns = \Schema::getColumnListing('students');
        return collect($request->all())
            ->only($columns)
            ->except(['id', 'created_at', 'updated_at', 'deleted_at'])
            ->all();
    }
}
