<?php

namespace App\Http\Controllers;

use App\Models\Account;
use App\Models\AdmissionApplication;
use App\Models\Branch;
use App\Models\Exam;
use App\Models\Family;
use App\Models\FeeChallan;
use App\Models\FeePayment;
use App\Models\Inquiry;
use App\Models\JournalLine;
use App\Models\Notice;
use App\Models\SchoolClass;
use App\Models\Student;
use App\Models\StudentAttendance;
use App\Models\User;
use App\Models\Visitor;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/**
 * ITA AI Assistant — a lightweight, on-device (no external API) helper that
 * answers natural-language questions by querying the application database.
 * Intent is resolved with keyword matching; every answer is branch-scoped via
 * the model global scopes and gated by the asking user's permissions.
 */
class AiAssistantController extends Controller
{
    public function ask(Request $request): JsonResponse
    {
        $user = $request->user();
        $raw = trim((string) $request->input('message', ''));
        $msg = mb_strtolower($raw);

        if ($msg === '') {
            return $this->reply('Ask me anything about your school data — try one of the suggestions below.', $this->starterChips());
        }

        // Intent routing (first match wins). Each handler returns a JsonResponse.
        $intents = [
            ['has' => ['help', 'what can you', 'how to use', 'commands'], 'fn' => 'intentHelp'],
            ['has' => ['hi', 'hello', 'hey', 'salam', 'assalam'], 'exactish' => true, 'fn' => 'intentGreeting'],
            ['has' => ['defaulter', 'unpaid', 'outstanding', 'dues', 'pending fee'], 'fn' => 'intentDefaulters'],
            ['has' => ['collect', 'collection', 'fee received', 'received fee', 'payment'], 'fn' => 'intentCollection'],
            ['has' => ['income'], 'fn' => 'intentIncome'],
            ['has' => ['expense', 'spending', 'spent'], 'fn' => 'intentExpense'],
            ['has' => ['profit', 'net'], 'fn' => 'intentProfit'],
            ['has' => ['attendance', 'present today', 'absent'], 'fn' => 'intentAttendance'],
            ['has' => ['new admission', 'admission this', 'admitted', 'admission'], 'fn' => 'intentAdmissions'],
            ['has' => ['inquir', 'enquir', 'lead'], 'fn' => 'intentInquiries'],
            ['has' => ['visitor', 'gate pass', 'front desk'], 'fn' => 'intentVisitors'],
            ['has' => ['notice', 'announcement', 'circular'], 'fn' => 'intentNotices'],
            ['has' => ['exam', 'test', 'result', 'datesheet'], 'fn' => 'intentExams'],
            ['has' => ['how many student', 'student count', 'total student', 'number of student'], 'fn' => 'intentStudentCount'],
            ['has' => ['teacher', 'staff'], 'fn' => 'intentTeacherCount'],
            ['has' => ['famil', 'parent', 'guardian'], 'fn' => 'intentFamilyCount'],
            ['has' => ['class', 'section'], 'fn' => 'intentClasses'],
            ['has' => ['branch', 'campus'], 'fn' => 'intentBranches'],
            ['has' => ['report'], 'fn' => 'intentReports'],
            ['has' => ['find student', 'search student', 'student named', 'show student'], 'fn' => 'intentFindStudent'],
            ['has' => ['find family', 'search family', 'find parent'], 'fn' => 'intentFindFamily'],
            ['has' => ['summary', 'overview', 'dashboard', 'stats', 'statistics'], 'fn' => 'intentSummary'],
        ];

        foreach ($intents as $intent) {
            $exactish = ! empty($intent['exactish']);
            foreach ($intent['has'] as $kw) {
                // "exactish" intents (e.g. greetings) match only on whole words,
                // so "hi" won't fire inside "this" / "this month".
                $hit = $exactish
                    ? (bool) preg_match('/\b'.preg_quote($kw, '/').'\b/u', $msg)
                    : str_contains($msg, $kw);
                if ($hit) {
                    return $this->{$intent['fn']}($msg, $raw, $user);
                }
            }
        }

        // Fallback: try a free-text lookup of students / families by the phrase.
        return $this->intentFreeSearch($msg, $raw, $user);
    }

    /* ───────────────────────── Intent handlers ───────────────────────── */

    private function intentHelp(string $msg, string $raw, $user): JsonResponse
    {
        return $this->reply(
            "I'm <b>ITA AI</b>, your on-site assistant. I read live data from your school database (no internet needed). You can ask me things like:".
            "<ul class='ai-list'>".
            "<li>How many students / teachers / families?</li>".
            "<li>Today's income, expense or profit</li>".
            "<li>Fee collection this month</li>".
            "<li>Show fee defaulters</li>".
            "<li>Find student Ahmed</li>".
            "<li>Attendance today</li>".
            "<li>New admissions this month</li>".
            "<li>Latest notices / upcoming exams</li>".
            "<li>Open the fee collection report</li>".
            "</ul>",
            $this->starterChips()
        );
    }

    private function intentGreeting(string $msg, string $raw, $user): JsonResponse
    {
        $name = explode(' ', trim($user->name))[0] ?? 'there';

        return $this->reply("Assalam-o-Alaikum, {$name}! 👋 How can I help you today?", $this->starterChips());
    }

    private function intentStudentCount(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('students.view')) {
            return $this->denied();
        }
        $active = Student::active()->count();
        $total = Student::count();

        return $this->reply(
            "You currently have <b>{$active}</b> active students (<b>{$total}</b> total including inactive).",
            ['All students', 'New admissions this month', 'Students by class'],
            $this->link('All students', 'students.index')
        );
    }

    private function intentTeacherCount(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('users.view')) {
            return $this->denied();
        }
        $teachers = User::whereHas('roles', fn ($q) => $q->where('name', 'Teacher'))->count();

        return $this->reply("There are <b>{$teachers}</b> teachers on record.", ['All users', 'Teacher attendance'], $this->link('All users', 'users.index'));
    }

    private function intentFamilyCount(string $msg, string $raw, $user): JsonResponse
    {
        // If it looks like a name lookup, defer to family search.
        if (preg_match('/(find|search|show)\s+(family|parent)/', $msg)) {
            return $this->intentFindFamily($msg, $raw, $user);
        }
        if (! $user->can('families.view')) {
            return $this->denied();
        }
        $families = Family::count();

        return $this->reply("There are <b>{$families}</b> registered families / parents.", ['All families', 'Find family'], $this->link('All families', 'families.index'));
    }

    private function intentClasses(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('academics.view')) {
            return $this->denied();
        }
        $classes = SchoolClass::withCount(['students' => fn ($q) => $q->where('status', 'active')])
            ->orderByDesc('students_count')->get();

        $items = $classes->take(8)->map(fn ($c) => [
            'title' => $c->name,
            'meta' => $c->students_count.' active student(s)',
            'url' => $this->url('classes.index'),
        ])->all();

        return $this->reply("You have <b>{$classes->count()}</b> classes. Here are the largest:", ['Subjects', 'Students by class'], null, $items);
    }

    private function intentBranches(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('branches.view')) {
            return $this->denied();
        }
        $branches = Branch::withCount('users')->get();
        $items = $branches->map(fn ($b) => [
            'title' => $b->name,
            'meta' => $b->code.' · '.$b->users_count.' user(s)',
            'url' => $this->url('branches.index'),
        ])->all();

        return $this->reply("There are <b>{$branches->count()}</b> branches:", ['All branches'], null, $items);
    }

    private function intentIncome(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('accounting.view')) {
            return $this->denied();
        }
        [$label, $range] = $this->resolvePeriod($msg);
        $amount = $this->periodSum('income', $range);

        return $this->reply("Income {$label}: <b>Rs ".number_format($amount)."</b>.", ['Expense '.$label, 'Profit '.$label, 'Profit & Loss report'], $this->link('Profit & Loss', 'reports.profit-loss'));
    }

    private function intentExpense(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('accounting.view')) {
            return $this->denied();
        }
        [$label, $range] = $this->resolvePeriod($msg);
        $amount = $this->periodSum('expense', $range);

        return $this->reply("Expenses {$label}: <b>Rs ".number_format($amount)."</b>.", ['Income '.$label, 'Profit '.$label], $this->link('Profit & Loss', 'reports.profit-loss'));
    }

    private function intentProfit(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('accounting.view')) {
            return $this->denied();
        }
        [$label, $range] = $this->resolvePeriod($msg);
        $income = $this->periodSum('income', $range);
        $expense = $this->periodSum('expense', $range);
        $profit = $income - $expense;
        $word = $profit >= 0 ? 'profit' : 'loss';

        return $this->reply(
            "Net {$word} {$label}: <b>Rs ".number_format(abs($profit))."</b><br><small class='text-muted'>Income Rs ".number_format($income)." − Expenses Rs ".number_format($expense)."</small>",
            ['Income '.$label, 'Expense '.$label, 'Balance sheet'],
            $this->link('Profit & Loss', 'reports.profit-loss')
        );
    }

    private function intentCollection(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('fees.view')) {
            return $this->denied();
        }
        [$label, $range] = $this->resolvePeriod($msg);
        $amount = (float) FeePayment::whereBetween('payment_date', $range)->sum('amount');
        $count = FeePayment::whereBetween('payment_date', $range)->count();

        return $this->reply(
            "Fee collection {$label}: <b>Rs ".number_format($amount)."</b> from <b>{$count}</b> payment(s).",
            ['Defaulters', 'Fee collection report'],
            $this->link('Fee collection report', 'reports.fee-collection')
        );
    }

    private function intentDefaulters(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('fees.view')) {
            return $this->denied();
        }
        $challans = FeeChallan::outstanding()->with('family')->get();
        $totalDue = $challans->sum(fn ($c) => $c->balance);

        $top = $challans->sortByDesc(fn ($c) => $c->balance)->take(6)->map(function ($c) {
            $who = $c->family?->primary_contact_name ?? 'Challan #'.$c->id;
            $code = $c->family?->family_code ? $c->family->family_code.' · ' : '';
            $url = $c->family
                ? $this->url('families.show', $c->family)
                : $this->url('fees.defaulters');

            return ['title' => $who, 'meta' => $code.'Due Rs '.number_format($c->balance), 'url' => $url];
        })->values()->all();

        if ($challans->isEmpty()) {
            return $this->reply('🎉 No outstanding fees — every challan is fully paid.', ['Fee collection this month']);
        }

        return $this->reply(
            "There are <b>{$challans->count()}</b> unpaid challans with <b>Rs ".number_format($totalDue)."</b> outstanding. Top dues:",
            ['Fee collection this month', 'Defaulters report'],
            $this->link('Open defaulters', 'fees.defaulters'),
            $top
        );
    }

    private function intentAttendance(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('attendance.view')) {
            return $this->denied();
        }
        $rows = StudentAttendance::whereDate('date', today())->get();
        if ($rows->isEmpty()) {
            return $this->reply("No student attendance has been marked for today yet.", ['Student attendance'], $this->link('Mark attendance', 'attendance.students.index'));
        }
        $present = $rows->whereIn('status', ['present', 'late', 'half_day'])->count();
        $absent = $rows->where('status', 'absent')->count();
        $pct = round($present / max($rows->count(), 1) * 100);

        return $this->reply(
            "Today's attendance: <b>{$present}</b> present, <b>{$absent}</b> absent (<b>{$pct}%</b> of {$rows->count()} marked).",
            ['Attendance report'],
            $this->link('Student attendance', 'attendance.students.index')
        );
    }

    private function intentAdmissions(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('students.view')) {
            return $this->denied();
        }
        $month = [now()->startOfMonth(), now()->endOfMonth()];
        $count = Student::whereBetween('admission_date', $month)->count();
        $pending = class_exists(AdmissionApplication::class) && Route::has('admissions.index')
            ? AdmissionApplication::query()->count() : null;

        $extra = $pending !== null ? " There are <b>{$pending}</b> admission applications in total." : '';

        return $this->reply("There were <b>{$count}</b> new admissions this month.{$extra}", ['All students', 'Inquiries'], $this->link('Admissions', 'admissions.index'));
    }

    private function intentInquiries(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('inquiries.view')) {
            return $this->denied();
        }
        $total = Inquiry::count();
        $thisMonth = Inquiry::whereBetween('created_at', [now()->startOfMonth(), now()->endOfMonth()])->count();

        return $this->reply("There are <b>{$total}</b> inquiries (<b>{$thisMonth}</b> this month).", ['Admissions'], $this->link('Inquiries', 'inquiries.index'));
    }

    private function intentVisitors(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('visitors.view')) {
            return $this->denied();
        }
        $today = Visitor::whereDate('created_at', today())->count();

        return $this->reply("There have been <b>{$today}</b> visitor(s) logged today.", ['Visitor log'], $this->link('Visitors', 'visitors.index'));
    }

    private function intentNotices(string $msg, string $raw, $user): JsonResponse
    {
        $notices = Notice::latest('id')->limit(5)->get();
        if ($notices->isEmpty()) {
            return $this->reply('There are no notices on the board yet.', null, $this->link('Notice board', 'notices.index'));
        }
        $items = $notices->map(fn ($n) => [
            'title' => $n->title,
            'meta' => optional($n->published_at ?? $n->created_at)->diffForHumans(),
            'url' => $this->url('notices.index'),
        ])->all();

        return $this->reply('Here are the latest notices:', ['Notice board'], $this->link('Notice board', 'notices.index'), $items);
    }

    private function intentExams(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('exams.view')) {
            return $this->denied();
        }
        $exams = Exam::latest('id')->limit(5)->get();
        if ($exams->isEmpty()) {
            return $this->reply('No exams have been created yet.', null, $this->link('Exams', 'exams.index'));
        }
        $items = $exams->map(fn ($e) => [
            'title' => $e->name ?? ('Exam #'.$e->id),
            'meta' => $e->session?->name ?? '',
            'url' => $this->url('exams.index'),
        ])->all();

        return $this->reply('Recent exams:', ['Result cards', 'Gazette sheet'], $this->link('Exams', 'exams.index'), $items);
    }

    private function intentReports(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('reports.view')) {
            return $this->denied();
        }
        $map = [
            'student' => ['Student report', 'reports.students'],
            'admission' => ['Admissions report', 'reports.admissions'],
            'fee' => ['Fee collection report', 'reports.fee-collection'],
            'collection' => ['Fee collection report', 'reports.fee-collection'],
            'defaulter' => ['Defaulters report', 'reports.defaulters'],
            'branch' => ['Branch summary', 'reports.branch-summary'],
        ];
        foreach ($map as $kw => [$label, $route]) {
            if (str_contains($msg, $kw) && Route::has($route)) {
                return $this->reply("Opening the <b>{$label}</b>.", null, $this->link($label, $route));
            }
        }

        return $this->reply(
            'Which report would you like? I can open any of these:',
            ['Student report', 'Fee collection report', 'Defaulters report', 'Branch summary'],
            $this->link('Reports center', 'reports.index')
        );
    }

    private function intentFindStudent(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('students.view')) {
            return $this->denied();
        }
        $term = $this->extractTerm($raw, ['find student', 'search student', 'show student', 'student named', 'student']);

        return $this->studentResults($term);
    }

    private function intentFindFamily(string $msg, string $raw, $user): JsonResponse
    {
        if (! $user->can('families.view')) {
            return $this->denied();
        }
        $term = $this->extractTerm($raw, ['find family', 'search family', 'find parent', 'family', 'parent']);

        return $this->familyResults($term);
    }

    private function intentSummary(string $msg, string $raw, $user): JsonResponse
    {
        $parts = [];
        if ($user->can('students.view')) {
            $parts[] = '👨‍🎓 <b>'.Student::active()->count().'</b> students';
        }
        if ($user->can('families.view')) {
            $parts[] = '👪 <b>'.Family::count().'</b> families';
        }
        if ($user->can('fees.view')) {
            $due = FeeChallan::outstanding()->get()->sum(fn ($c) => $c->balance);
            $parts[] = '💰 <b>Rs '.number_format($due).'</b> outstanding';
        }
        if ($user->can('accounting.view')) {
            $month = [now()->startOfMonth(), now()->endOfMonth()];
            $profit = $this->periodSum('income', $month) - $this->periodSum('expense', $month);
            $parts[] = '📈 <b>Rs '.number_format($profit).'</b> profit this month';
        }
        if (empty($parts)) {
            return $this->reply("Here's your dashboard.", null, $this->link('Open dashboard', 'dashboard'));
        }

        return $this->reply('Quick snapshot:<br>'.implode('<br>', $parts), ['Fee collection this month', 'Defaulters'], $this->link('Open dashboard', 'dashboard'));
    }

    private function intentFreeSearch(string $msg, string $raw, $user): JsonResponse
    {
        // Try students then families using the whole phrase.
        if ($user->can('students.view')) {
            $r = $this->studentResults($raw, false);
            if ($r !== null) {
                return $r;
            }
        }
        if ($user->can('families.view')) {
            $r = $this->familyResults($raw, false);
            if ($r !== null) {
                return $r;
            }
        }

        return $this->reply(
            "I'm not sure about that one. I can answer questions about students, fees, income, attendance, admissions and reports. Try a suggestion below or type <i>help</i>.",
            $this->starterChips()
        );
    }

    /* ───────────────────────── Search helpers ───────────────────────── */

    private function studentResults(string $term, bool $emptyMessage = true): ?JsonResponse
    {
        $term = trim($term);
        if (mb_strlen($term) < 2) {
            return $emptyMessage ? $this->reply('Who would you like to find? Type the student name, e.g. <i>find student Ahmed</i>.') : null;
        }
        $like = '%'.$term.'%';
        $students = Student::with(['schoolClass', 'section'])
            ->where(fn ($w) => $w->where('name', 'like', $like)
                ->orWhere('admission_number', 'like', $like)
                ->orWhere('b_form_number', 'like', $like))
            ->orderBy('name')->limit(6)->get();

        if ($students->isEmpty()) {
            return $emptyMessage ? $this->reply("No students matched “".e($term)."”.") : null;
        }
        $items = $students->map(fn ($s) => [
            'title' => $s->name,
            'meta' => trim(($s->admission_number ?? '').' · '.($s->schoolClass?->name ?? 'No class')),
            'url' => $this->url('students.show', $s),
        ])->all();

        return $this->reply("Found <b>{$students->count()}</b> student(s) matching “".e($term)."”:", null, null, $items);
    }

    private function familyResults(string $term, bool $emptyMessage = true): ?JsonResponse
    {
        $term = trim($term);
        if (mb_strlen($term) < 2) {
            return $emptyMessage ? $this->reply('Type the family name or code, e.g. <i>find family Khan</i>.') : null;
        }
        $like = '%'.$term.'%';
        $families = Family::withCount('students')
            ->where(fn ($w) => $w->where('family_code', 'like', $like)
                ->orWhere('father_name', 'like', $like)
                ->orWhere('mother_name', 'like', $like)
                ->orWhere('guardian_name', 'like', $like))
            ->limit(6)->get();

        if ($families->isEmpty()) {
            return $emptyMessage ? $this->reply("No families matched “".e($term)."”.") : null;
        }
        $items = $families->map(fn ($f) => [
            'title' => $f->primary_contact_name,
            'meta' => $f->family_code.' · '.$f->students_count.' student(s)',
            'url' => $this->url('families.show', $f),
        ])->all();

        return $this->reply("Found <b>{$families->count()}</b> family(ies):", null, null, $items);
    }

    /* ───────────────────────── Utilities ───────────────────────── */

    /** Resolve a time-period from the message: today (default) vs this month vs this year. */
    private function resolvePeriod(string $msg): array
    {
        if (str_contains($msg, 'month')) {
            return ['this month', [now()->startOfMonth(), now()->endOfMonth()]];
        }
        if (str_contains($msg, 'year')) {
            return ['this year', [now()->startOfYear(), now()->endOfYear()]];
        }
        if (str_contains($msg, 'week')) {
            return ['this week', [now()->startOfWeek(), now()->endOfWeek()]];
        }
        if (str_contains($msg, 'yesterday')) {
            return ['yesterday', [now()->subDay()->startOfDay(), now()->subDay()->endOfDay()]];
        }

        return ['today', [today(), today()->endOfDay()]];
    }

    /** Net activity for income/expense accounts within a [from, to] period. */
    private function periodSum(string $type, array $range): float
    {
        $accountIds = Account::type($type)->pluck('id');
        if ($accountIds->isEmpty()) {
            return 0.0;
        }
        $q = JournalLine::whereIn('account_id', $accountIds)
            ->whereHas('entry', fn ($e) => $e->whereBetween('date', $range));
        $debit = (float) $q->sum('debit');
        $credit = (float) $q->sum('credit');

        return round($type === 'income' ? $credit - $debit : $debit - $credit, 2);
    }

    private function extractTerm(string $raw, array $prefixes): string
    {
        $term = $raw;
        foreach ($prefixes as $p) {
            $term = preg_replace('/'.preg_quote($p, '/').'/i', '', $term);
        }

        return trim(preg_replace('/\s+/', ' ', $term));
    }

    private function url(string $route, $param = null): string
    {
        if (! Route::has($route)) {
            return '#';
        }

        return $param !== null ? route($route, $param) : route($route);
    }

    /** Build a primary-action link payload, or null if the route is missing. */
    private function link(string $label, string $route): ?array
    {
        return Route::has($route) ? ['label' => $label, 'url' => route($route)] : null;
    }

    private function starterChips(): array
    {
        return ['Summary', 'How many students?', "Today's income", 'Show defaulters', 'Find student'];
    }

    private function denied(): JsonResponse
    {
        return $this->reply("You don't have permission to view that information.");
    }

    /**
     * Standard reply envelope.
     *
     * @param  string  $text  HTML-safe message body
     * @param  array|null  $chips  suggested follow-up prompts
     * @param  array|null  $action  primary link ['label','url']
     * @param  array|null  $items  list rows [['title','meta','url'], ...]
     */
    private function reply(string $text, ?array $chips = null, ?array $action = null, ?array $items = null): JsonResponse
    {
        return response()->json([
            'reply' => $text,
            'chips' => $chips ?? [],
            'action' => $action,
            'items' => $items ?? [],
        ]);
    }
}
