<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Models\FeeChallan;
use App\Models\FeePayment;
use App\Models\SchoolClass;
use App\Models\Student;
use App\Models\StudentAttendance;
use App\Support\ReportExporter;
use Illuminate\Http\Request;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
use Illuminate\Support\Carbon;

class ReportController extends Controller implements HasMiddleware
{
    public function __construct(private ReportExporter $exporter)
    {
    }

    public static function middleware(): array
    {
        return [new Middleware('can:reports.view')];
    }

    public function index()
    {
        return view('reports.index');
    }

    /* ---------------- Student list ---------------- */
    public function students(Request $request)
    {
        $filters = $request->only(['class_id', 'status', 'gender']);

        $students = Student::with(['schoolClass', 'section', 'family'])
            ->when($filters['class_id'] ?? null, fn ($q, $v) => $q->where('class_id', $v))
            ->when($filters['status'] ?? null, fn ($q, $v) => $q->where('status', $v))
            ->when($filters['gender'] ?? null, fn ($q, $v) => $q->where('gender', $v))
            ->orderBy('name')->get();

        $headings = ['Adm #', 'Name', 'Class', 'Section', 'Gender', 'Father Name', 'Family', 'Status'];
        $rows = $students->map(fn ($s) => [
            $s->admission_number, $s->name, $s->schoolClass?->name ?? '-',
            $s->section?->name ?? '-', ucfirst($s->gender),
            $s->family?->father_name ?: '-', $s->family?->family_code,
            ucfirst(str_replace('_', ' ', $s->status)),
        ]);

        return $this->respond($request, 'Student Report', $headings, $rows, [
            'Total' => $students->count(),
        ], view: 'reports.students', viewData: [
            'students' => $students, 'classes' => SchoolClass::active()->orderBy('level')->get(), 'filters' => $filters,
        ]);
    }

    /* ---------------- Admissions over a period ---------------- */
    public function admissions(Request $request)
    {
        $from = $request->date('from') ?: Carbon::today()->startOfYear();
        $to = $request->date('to') ?: Carbon::today();

        $students = Student::with(['schoolClass', 'family'])
            ->whereBetween('admission_date', [$from, $to])
            ->orderBy('admission_date')->get();

        $headings = ['Adm #', 'Name', 'Class', 'Admission Date', 'Family', 'Status'];
        $rows = $students->map(fn ($s) => [
            $s->admission_number, $s->name, $s->schoolClass?->name ?? '-',
            optional($s->admission_date)->format('d M Y'), $s->family?->family_code,
            ucfirst(str_replace('_', ' ', $s->status)),
        ]);

        return $this->respond($request, 'Admissions Report', $headings, $rows, [
            'Period' => $from->format('d M Y').' – '.$to->format('d M Y'), 'New Admissions' => $students->count(),
        ], view: 'reports.admissions', viewData: [
            'students' => $students, 'from' => $from->toDateString(), 'to' => $to->toDateString(),
        ]);
    }

    /* ---------------- Fee collection over a period ---------------- */
    public function feeCollection(Request $request)
    {
        $from = $request->date('from') ?: Carbon::today()->startOfMonth();
        $to = $request->date('to') ?: Carbon::today();
        $search = trim((string) $request->input('search', ''));

        $payments = FeePayment::with(['challan.family', 'challan.items.student.schoolClass', 'paymentMethod', 'receiver'])
            ->whereBetween('payment_date', [$from, $to])
            ->when($search, function ($q, $term) {
                $like = '%'.$term.'%';
                $q->where(function ($w) use ($like) {
                    $w->where('receipt_number', 'like', $like)
                        ->orWhereHas('challan', fn ($c) => $c->where('challan_number', 'like', $like)
                            ->orWhereHas('family', fn ($f) => $f->where('family_code', 'like', $like)->orWhere('father_name', 'like', $like))
                            ->orWhereHas('items.student', fn ($s) => $s->where('name', 'like', $like)));
                });
            })
            ->orderBy('payment_date')->get();

        // Student name(s) and class(es) covered by each payment's challan.
        $studentNames = fn ($p) => $p->challan?->items->map(fn ($i) => $i->student?->name)->filter()->unique()->implode(', ') ?: '-';
        $classNames = fn ($p) => $p->challan?->items->map(fn ($i) => $i->student?->schoolClass?->name)->filter()->unique()->implode(', ') ?: '-';

        $headings = ['Student', 'Date', 'Challan', 'Class', 'Method', 'Amount'];
        $rows = $payments->map(fn ($p) => [
            $studentNames($p), $p->payment_date->format('d M Y'), $p->challan?->challan_number,
            $classNames($p), $p->method_label, number_format($p->amount, 2),
        ]);

        $total = $payments->sum('amount');

        return $this->respond($request, 'Fee Collection Report', $headings, $rows, [
            'Period' => $from->format('d M Y').' – '.$to->format('d M Y'),
            'Transactions' => $payments->count(), 'Total Collected' => number_format($total, 2),
        ], view: 'reports.fee-collection', viewData: [
            'payments' => $payments, 'total' => $total,
            'byMethod' => $payments->groupBy('method')->map->sum('amount'),
            'from' => $from->toDateString(), 'to' => $to->toDateString(), 'search' => $search,
            'studentNames' => $studentNames, 'classNames' => $classNames,
        ]);
    }

    /* ---------------- Defaulters ---------------- */
    public function defaulters(Request $request)
    {
        $challans = FeeChallan::with('family')->outstanding()
            ->whereDate('due_date', '<', now())->orderBy('due_date')->get();

        $headings = ['Challan', 'Family', 'Contact', 'Title', 'Due Date', 'Days Overdue', 'Balance'];
        $rows = $challans->map(fn ($c) => [
            $c->challan_number, $c->family?->family_code, $c->family?->primary_mobile, $c->title,
            optional($c->due_date)->format('d M Y'), (int) now()->diffInDays($c->due_date), number_format($c->balance, 2),
        ]);

        $total = $challans->sum(fn ($c) => $c->balance);

        return $this->respond($request, 'Fee Defaulters Report', $headings, $rows, [
            'Overdue Challans' => $challans->count(), 'Total Outstanding' => number_format($total, 2),
        ], view: 'reports.defaulters', viewData: ['challans' => $challans, 'total' => $total]);
    }

    /* ---------------- Branch-wise summary ---------------- */
    public function branchSummary(Request $request)
    {
        $branches = Branch::all()->map(function ($b) {
            $students = Student::withoutGlobalScopes()->where('branch_id', $b->id);
            $revenue = FeePayment::withoutGlobalScopes()->where('branch_id', $b->id)->sum('amount');
            $outstanding = FeeChallan::withoutGlobalScopes()->where('branch_id', $b->id)
                ->whereIn('status', ['unpaid', 'partial'])->get()->sum(fn ($c) => $c->balance);

            return [
                'branch' => $b,
                'students' => (clone $students)->count(),
                'active' => (clone $students)->where('status', 'active')->count(),
                'revenue' => $revenue,
                'outstanding' => $outstanding,
            ];
        });

        $headings = ['Branch', 'Code', 'Students', 'Active', 'Revenue', 'Outstanding'];
        $rows = $branches->map(fn ($r) => [
            $r['branch']->name, $r['branch']->code, $r['students'], $r['active'],
            number_format($r['revenue'], 2), number_format($r['outstanding'], 2),
        ]);

        return $this->respond($request, 'Branch-wise Summary', $headings, $rows, [], view: 'reports.branch-summary', viewData: ['branches' => $branches]);
    }

    /* ---------------- Format dispatcher ---------------- */
    private function respond(Request $request, string $title, array $headings, $rows, array $meta, string $view, array $viewData)
    {
        $rowsArray = collect($rows)->map(fn ($r) => array_values((array) $r))->all();
        $slug = \Illuminate\Support\Str::slug($title);

        return match ($request->input('export')) {
            'csv' => $this->exporter->csv($slug, $headings, $rowsArray),
            'pdf' => $this->exporter->pdf($slug, $title, $headings, $rowsArray, $meta, count($headings) > 6 ? 'landscape' : 'portrait'),
            default => view($view, array_merge($viewData, ['title' => $title, 'meta' => $meta])),
        };
    }
}
