<?php

namespace App\Services;

use App\Models\CustomerAppointment;
use App\Models\CustomerDocument;
use App\Models\Loan;
use App\Models\LoanInstallment;
use App\Models\PayrollPeriod;
use App\Models\RecurringExpense;
use App\Models\Task;
use Carbon\CarbonImmutable;

class ManagementCalendarService
{
    public const TYPES = [
        'loan_due' => 'Loan due dates',
        'loan_maturity' => 'Loan maturity',
        'task' => 'Tasks',
        'appointment' => 'Customer appointments',
        'payroll' => 'Payroll',
        'expense_due' => 'Expense due dates',
        'document_expiry' => 'Document expiries',
    ];

    public function __construct(private BranchContext $branchContext)
    {
    }

    public function events(array $branchIds, string $from, string $to): array
    {
        return $this->branchContext->forBranches($branchIds, function () use ($branchIds, $from, $to): array {
            $fromDate = CarbonImmutable::parse($from)->startOfDay();
            $toDate = CarbonImmutable::parse($to)->endOfDay();
            $events = [];

            $installments = LoanInstallment::query()->with(['loan.customer','loan.branch'])
                ->where('is_active',true)->where('remaining','>',0)
                ->whereHas('loan', fn ($q) => $q->whereIn('branch_id',$branchIds)->where('status','active'))
                ->whereDate('due_date','>=',$fromDate->toDateString())->whereDate('due_date','<=',$toDate->toDateString())->get();
            foreach ($installments as $installment) {
                $events[] = $this->event($installment->due_date, 'loan_due', 'Loan due · '.$installment->loan->loan_number,
                    ($installment->loan->customer?->full_name ?: 'Customer').' · Remaining '.$installment->remaining,
                    $installment->loan->branch?->name, route('loans.show',$installment->loan), $installment->status);
            }

            $loans = Loan::query()->with(['allInstallments','customer','branch'])->whereIn('branch_id',$branchIds)->where('status','active')->get();
            foreach ($loans as $loan) {
                $last = $loan->allInstallments->where('is_active',true)->sortByDesc(fn ($row) => $row->due_date?->toDateString())->first();
                if (! $last?->due_date) continue;
                $maturity = CarbonImmutable::parse($last->due_date);
                if ($maturity->lt($fromDate) || $maturity->gt($toDate)) continue;
                $events[] = $this->event($maturity, 'loan_maturity', 'Loan maturity · '.$loan->loan_number,
                    $loan->customer?->full_name ?: 'Customer', $loan->branch?->name, route('loans.show',$loan), $loan->status);
            }

            foreach (Task::query()->with(['branch','assignee'])->whereIn('branch_id',$branchIds)->whereNotNull('due_at')
                ->whereBetween('due_at',[$fromDate,$toDate])->get() as $task) {
                $events[] = $this->event($task->due_at, 'task', $task->title,
                    ($task->assignee?->name ? 'Assigned to '.$task->assignee->name : 'Unassigned').' · '.str($task->status)->replace('_',' ')->headline(),
                    $task->branch?->name, route('tasks.show',$task), $task->status);
            }

            foreach (CustomerAppointment::query()->with(['branch','customer','staff'])->whereIn('branch_id',$branchIds)
                ->whereBetween('scheduled_at',[$fromDate,$toDate])->get() as $appointment) {
                $events[] = $this->event($appointment->scheduled_at, 'appointment', $appointment->title,
                    ($appointment->customer?->full_name ?: 'Customer').($appointment->staff?->name ? ' · '.$appointment->staff->name : ''),
                    $appointment->branch?->name, route('calendar.index',['from'=>$fromDate->toDateString(),'to'=>$toDate->toDateString()]), $appointment->status);
            }

            foreach (PayrollPeriod::query()->with('branch')->whereIn('branch_id',$branchIds)->whereNotNull('pay_date')
                ->whereDate('pay_date','>=',$fromDate->toDateString())->whereDate('pay_date','<=',$toDate->toDateString())->get() as $period) {
                $events[] = $this->event($period->pay_date, 'payroll', 'Payroll · '.$period->name,
                    $period->period_number.' · Net '.$period->total_net, $period->branch?->name, route('payroll.show',$period), $period->status);
            }

            foreach (RecurringExpense::query()->with(['branch','category'])->whereIn('branch_id',$branchIds)->where('status','active')
                ->whereDate('next_due_date','>=',$fromDate->toDateString())->whereDate('next_due_date','<=',$toDate->toDateString())->get() as $expense) {
                $events[] = $this->event($expense->next_due_date, 'expense_due', 'Expense due · '.($expense->category?->name ?: 'Expense'),
                    $expense->description.' · '.$expense->amount, $expense->branch?->name, route('recurring-expenses.edit',$expense), $expense->status);
            }

            foreach (CustomerDocument::query()->with('customer.branch')->whereHas('customer',fn ($q) => $q->whereIn('branch_id',$branchIds))
                ->whereNotNull('expiry_date')->whereDate('expiry_date','>=',$fromDate->toDateString())->whereDate('expiry_date','<=',$toDate->toDateString())->get() as $document) {
                $events[] = $this->event($document->expiry_date, 'document_expiry', 'Document expiry · '.$document->document_type,
                    $document->customer?->full_name ?: 'Customer', $document->customer?->branch?->name, route('customers.show',$document->customer), 'expiring');
            }

            usort($events, fn (array $a, array $b) => [$a['sort_at'],$a['type'],$a['title']] <=> [$b['sort_at'],$b['type'],$b['title']]);
            return $events;
        });
    }

    private function event(mixed $date, string $type, string $title, string $subtitle, ?string $branch, string $url, ?string $status): array
    {
        $at = CarbonImmutable::parse($date);
        return [
            'date'=>$at->toDateString(),'time'=>$at->format('H:i') === '00:00' ? null : $at->format('H:i'),
            'sort_at'=>$at->format('Y-m-d H:i:s'),'type'=>$type,'type_label'=>self::TYPES[$type],
            'title'=>$title,'subtitle'=>$subtitle,'branch'=>$branch ?: '—','url'=>$url,'status'=>$status ?: '—',
        ];
    }
}
