<?php

namespace App\Http\Controllers;

use App\Models\AcademicSession;
use App\Models\Family;
use App\Models\FeeChallan;
use App\Models\FeeType;
use App\Services\FeeService;
use App\Support\BranchContext;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
use Illuminate\Support\Carbon;
use Illuminate\View\View;

class FeeChallanController extends Controller implements HasMiddleware
{
    public function __construct(private FeeService $fees)
    {
    }

    public static function middleware(): array
    {
        return [
            new Middleware('can:fees.view', only: ['index', 'show', 'print', 'defaulters']),
            new Middleware('can:fees.create', only: ['create', 'store']),
            new Middleware('can:fees.update', only: ['waive']),
            new Middleware('can:fees.delete', only: ['destroy']),
        ];
    }

    public function index(Request $request): View
    {
        $status = $request->input('status');

        $challans = FeeChallan::with('family')
            ->when($status, fn ($q) => $q->where('status', $status))
            ->latest()->paginate(20)->withQueryString();

        return view('fees.challans.index', compact('challans', 'status'));
    }

    public function create(): View
    {
        return view('fees.challans.create', [
            'sessions' => AcademicSession::orderByDesc('id')->get(),
            'feeTypes' => FeeType::active()->orderBy('name')->get(),
            'families' => Family::orderBy('family_code')->get(),
            'classes' => \App\Models\SchoolClass::active()->orderBy('level')->get(),
            'sections' => \App\Models\Section::active()->get(['id', 'class_id', 'name']),
            'studentsList' => \App\Models\Student::where('status', 'active')
                ->with('family:id,father_name')
                ->orderBy('name')
                ->get(['id', 'name', 'admission_number', 'b_form_number', 'class_id', 'section_id', 'family_id'])
                ->map(fn ($s) => [
                    'id' => $s->id, 'name' => $s->name, 'admission_number' => $s->admission_number,
                    'nic' => $s->b_form_number, 'father' => $s->family?->father_name,
                    'class_id' => $s->class_id, 'section_id' => $s->section_id, 'family_id' => $s->family_id,
                ]),
        ]);
    }

    public function store(Request $request, BranchContext $context): RedirectResponse
    {
        $data = $request->validate([
            'academic_session_id' => ['required', 'exists:academic_sessions,id'],
            'month' => ['required', 'date'],
            'bill_date' => ['required', 'date'],
            'due_date' => ['required', 'date', 'after_or_equal:bill_date'],
            'fee_type_ids' => ['required', 'array', 'min:1'],
            'fee_type_ids.*' => ['exists:fee_types,id'],
            'scope' => ['required', 'in:branch,family,students'],
            'family_id' => ['required_if:scope,family', 'nullable', 'exists:families,id'],
            'student_ids' => ['required_if:scope,students', 'array'],
            'student_ids.*' => ['exists:students,id'],
        ]);

        $session = AcademicSession::findOrFail($data['academic_session_id']);
        $month = Carbon::parse($data['month']);
        $billDate = Carbon::parse($data['bill_date']);
        $dueDate = Carbon::parse($data['due_date']);

        if ($data['scope'] === 'students') {
            $count = $this->fees->generateForStudents($data['student_ids'] ?? [], $session, $month, $data['fee_type_ids'], $billDate, $dueDate);
            if ($count === 0) {
                return back()->with('error', 'No challans generated — the selected students may already be billed for this month or have no billable fees.');
            }

            return redirect()->route('challans.index')->with('success', "$count challan(s) generated for the selected students.");
        }

        if ($data['scope'] === 'family') {
            $family = Family::findOrFail($data['family_id']);
            $challan = $this->fees->generateFamilyChallan($family, $session, $month, $data['fee_type_ids'], $billDate, $dueDate);

            if (! $challan) {
                return back()->with('error', 'No billable items for this family (check active students and fee structure).');
            }

            return redirect()->route('challans.show', $challan)->with('success', 'Challan generated.');
        }

        $branchId = $context->currentId() ?? auth()->user()->branch_id ?? \App\Models\Branch::min('id');
        if (! $branchId) {
            return back()->with('error', 'No branch is available to bill. Please set up a branch first.');
        }
        $count = $this->fees->generateForBranch((int) $branchId, $session, $month, $data['fee_type_ids'], $billDate, $dueDate);

        return redirect()->route('challans.index')->with('success', "$count challan(s) generated for ".$month->format('F Y').'.');
    }

    public function show(FeeChallan $challan): View
    {
        $challan->load(['family', 'items.student', 'items.feeType', 'payments.receiver', 'payments.paymentMethod']);

        return view('fees.challans.show', compact('challan'));
    }

    public function print(FeeChallan $challan): View
    {
        $challan->load(['family', 'items.student', 'items.feeType', 'branch', 'payments.paymentMethod']);

        // Yearly history (family account) for the challan's billing year — same as the Fee Card.
        $year = (int) ($challan->billing_month?->format('Y') ?: now()->year);
        $billed = array_fill(1, 12, 0.0);
        $paid = array_fill(1, 12, 0.0);
        $yearChallans = FeeChallan::where('family_id', $challan->family_id)
            ->whereYear('billing_month', $year)->get();
        foreach ($yearChallans as $c) {
            $m = (int) $c->billing_month->format('n');
            $billed[$m] += $c->net_payable;
            $paid[$m] += (float) $c->paid_amount;
        }

        // Thermal / receipt printer — a single narrow Parent copy.
        if (request()->boolean('thermal')) {
            return view('fees.challans.thermal', compact('challan', 'year', 'billed', 'paid'));
        }

        return view('fees.challans.print', compact('challan', 'year', 'billed', 'paid'));
    }

    public function waive(FeeChallan $challan): RedirectResponse
    {
        $this->fees->waive($challan);

        return back()->with('success', 'Challan waived.');
    }

    public function destroy(FeeChallan $challan): RedirectResponse
    {
        if ($challan->payments()->exists()) {
            return back()->with('error', 'Cannot delete a challan that has payments.');
        }
        $challan->delete();

        return redirect()->route('challans.index')->with('success', 'Challan deleted.');
    }

    /**
     * Quick lookup of a challan by number (or family code) for the collect-payment
     * popup on the challans list. Returns JSON with the challan's payable details.
     */
    public function lookup(Request $request): \Illuminate\Http\JsonResponse
    {
        $q = trim((string) $request->input('q', ''));
        if ($q === '') {
            return response()->json(['found' => false, 'message' => 'Enter a challan number.']);
        }

        $challan = FeeChallan::with(['family', 'items.student'])
            ->where('challan_number', $q)
            ->orWhere('challan_number', 'like', '%'.$q.'%')
            ->orderByRaw("challan_number = ? desc", [$q])
            ->first();

        if (! $challan) {
            return response()->json(['found' => false, 'message' => 'No challan found for “'.$q.'”.']);
        }

        $methods = \App\Models\PaymentMethod::withoutGlobalScopes()
            ->where('branch_id', $challan->branch_id)->where('is_active', true)
            ->orderBy('sort_order')->orderBy('name')->get()
            ->map(fn ($m) => ['id' => $m->id, 'label' => $m->display_label, 'account' => $m->account_number]);

        return response()->json([
            'found' => true,
            'challan' => [
                'id' => $challan->id,
                'number' => $challan->challan_number,
                'family' => $challan->family?->family_code.' — '.$challan->family?->primary_contact_name,
                'students' => $challan->items->map(fn ($i) => $i->student?->name)->filter()->unique()->implode(', '),
                'title' => $challan->title,
                'net' => (float) $challan->net_payable,
                'paid' => (float) $challan->paid_amount,
                'balance' => (float) $challan->balance,
                'status' => $challan->status,
                'settled' => in_array($challan->status, ['paid', 'waived'], true),
                'pay_url' => route('challans.payments.store', $challan),
                'show_url' => route('challans.show', $challan),
            ],
            'methods' => $methods,
        ]);
    }

    /** Families/challans with an outstanding balance. */
    public function defaulters(Request $request)
    {
        $filters = $request->only(['search', 'class_id', 'section_id', 'from', 'to', 'scope']);
        $scope = $filters['scope'] ?? 'overdue'; // overdue | all

        $query = FeeChallan::with('family')->outstanding();

        // Date range on due date, or default to overdue-only.
        if (! empty($filters['from'])) {
            $query->whereDate('due_date', '>=', $filters['from']);
        }
        if (! empty($filters['to'])) {
            $query->whereDate('due_date', '<=', $filters['to']);
        }
        if (empty($filters['from']) && empty($filters['to']) && $scope === 'overdue') {
            $query->whereDate('due_date', '<', now());
        }

        // Class / section — challans whose items include a student in that class/section.
        if (! empty($filters['class_id'])) {
            $query->whereHas('items.student', fn ($q) => $q->where('class_id', $filters['class_id']));
        }
        if (! empty($filters['section_id'])) {
            $query->whereHas('items.student', fn ($q) => $q->where('section_id', $filters['section_id']));
        }

        // Standalone search — challan number, family code, contact names & mobile.
        if (! empty($filters['search'])) {
            $like = '%'.$filters['search'].'%';
            $query->where(function ($w) use ($like) {
                $w->where('challan_number', 'like', $like)
                    ->orWhereHas('family', fn ($f) => $f->where('family_code', 'like', $like)
                        ->orWhere('father_name', 'like', $like)
                        ->orWhere('mother_name', 'like', $like)
                        ->orWhere('guardian_name', 'like', $like)
                        ->orWhere('father_mobile', 'like', $like));
            });
        }

        $challans = $query->orderBy('due_date')->get();
        $totalOutstanding = $challans->sum(fn ($c) => $c->balance);

        // PDF / Excel export (respects the active filters).
        if (in_array($request->input('export'), ['pdf', 'csv'], true)) {
            $exporter = app(\App\Support\ReportExporter::class);
            $headings = ['Challan', 'Family', 'Contact', 'Title', 'Due Date', 'Days Overdue', 'Balance'];
            $rows = $challans->map(fn ($c) => [
                $c->challan_number, $c->family?->primary_contact_name, $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),
            ])->all();

            return $request->input('export') === 'csv'
                ? $exporter->csv('fee-defaulters', $headings, $rows)
                : $exporter->pdf('fee-defaulters', 'Fee Defaulters', $headings, $rows, ['Total Outstanding' => number_format($totalOutstanding, 2)], 'landscape');
        }

        $classes = \App\Models\SchoolClass::query()->orderBy('level')->get(['id', 'name']);
        $sections = \App\Models\Section::query()
            ->when($filters['class_id'] ?? null, fn ($q, $v) => $q->where('class_id', $v))
            ->orderBy('name')->get(['id', 'name', 'class_id']);

        return view('fees.defaulters', compact('challans', 'totalOutstanding', 'filters', 'classes', 'sections'));
    }
}
