<?php

namespace App\Services\Care\Dentistry;

use App\Models\Appointment;
use App\Models\BillLineItem;
use App\Models\DentalImage;
use App\Models\DentalPlanItem;
use App\Models\DentalProcedure;
use App\Models\DentalRecall;
use App\Models\DentalTreatmentPlan;
use App\Models\Organization;
use App\Models\Patient;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class DentalAnalyticsService
{
    /**
     * @return array{
     *     procedures_today: Collection,
     *     procedures_month: Collection,
     *     revenue_by_code: Collection,
     *     unfinished_items: int,
     *     avg_chair_minutes: ?float,
     *     imaging_by_modality: Collection,
     *     from: string,
     *     to: string
     * }
     */
    public function report(
        Organization $organization,
        string $ownerRef,
        ?int $branchId = null,
        ?string $from = null,
        ?string $to = null,
    ): array {
        $todayStart = now()->startOfDay();
        $rangeFrom = $from ? Carbon::parse($from)->startOfDay() : now()->startOfMonth();
        $rangeTo = $to ? Carbon::parse($to)->endOfDay() : now()->endOfDay();

        $procBase = DentalProcedure::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('status', DentalProcedure::STATUS_COMPLETED);

        $proceduresToday = (clone $procBase)
            ->where('performed_at', '>=', $todayStart)
            ->selectRaw('procedure_code, label, count(*) as total, sum(amount_minor) as amount_minor')
            ->groupBy('procedure_code', 'label')
            ->orderByDesc('total')
            ->get();

        $proceduresMonth = (clone $procBase)
            ->whereBetween('performed_at', [$rangeFrom, $rangeTo])
            ->selectRaw('procedure_code, label, count(*) as total, sum(amount_minor) as amount_minor')
            ->groupBy('procedure_code', 'label')
            ->orderByDesc('total')
            ->get();

        $revenueByCode = BillLineItem::query()
            ->where('owner_ref', $ownerRef)
            ->where('source_type', 'dental_procedure')
            ->whereBetween('created_at', [$rangeFrom, $rangeTo])
            ->selectRaw('description, sum(total_minor) as amount_minor, count(*) as total')
            ->groupBy('description')
            ->orderByDesc('amount_minor')
            ->limit(20)
            ->get();

        $unfinished = DentalPlanItem::query()
            ->whereHas('plan', function ($q) use ($organization, $ownerRef) {
                $q->owned($ownerRef)
                    ->where('organization_id', $organization->id)
                    ->whereIn('status', [
                        DentalTreatmentPlan::STATUS_ACCEPTED,
                        DentalTreatmentPlan::STATUS_IN_PROGRESS,
                    ]);
            })
            ->whereIn('status', [DentalPlanItem::STATUS_ACCEPTED, DentalPlanItem::STATUS_PROPOSED])
            ->count();

        $chairQuery = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('status', Appointment::STATUS_COMPLETED)
            ->whereNotNull('started_at')
            ->whereNotNull('completed_at')
            ->whereBetween('completed_at', [$rangeFrom, $rangeTo])
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId));

        $avgChair = null;
        $samples = (clone $chairQuery)->limit(200)->get(['started_at', 'completed_at']);
        if ($samples->isNotEmpty()) {
            $avgChair = round($samples->avg(fn ($a) => $a->started_at->diffInMinutes($a->completed_at)), 1);
        }

        $imaging = DentalImage::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereBetween('captured_at', [$rangeFrom, $rangeTo])
            ->selectRaw('modality, count(*) as total')
            ->groupBy('modality')
            ->orderByDesc('total')
            ->get();

        return [
            'procedures_today' => $proceduresToday,
            'procedures_month' => $proceduresMonth,
            'revenue_by_code' => $revenueByCode,
            'unfinished_items' => $unfinished,
            'avg_chair_minutes' => $avgChair,
            'imaging_by_modality' => $imaging,
            'from' => $rangeFrom->toDateString(),
            'to' => $rangeTo->toDateString(),
        ];
    }

    /**
     * @return list<array{code: string, severity: string, message: string}>
     */
    public function alertsForPatient(Organization $organization, Patient $patient, string $ownerRef): array
    {
        $alerts = [];

        $plan = DentalTreatmentPlan::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('patient_id', $patient->id)
            ->whereIn('status', [
                DentalTreatmentPlan::STATUS_ACCEPTED,
                DentalTreatmentPlan::STATUS_IN_PROGRESS,
            ])
            ->with('items')
            ->latest('id')
            ->first();

        if ($plan && $plan->accepted_at && $plan->accepted_at->lt(now()->subDays(30))) {
            $open = $plan->items->whereIn('status', [
                DentalPlanItem::STATUS_ACCEPTED,
                DentalPlanItem::STATUS_PROPOSED,
            ])->count();
            if ($open > 0) {
                $alerts[] = [
                    'code' => 'dental.plan.stale',
                    'severity' => 'warning',
                    'message' => "Accepted treatment plan has {$open} unfinished item(s) older than 30 days.",
                ];
            }
        }

        if ($plan) {
            $high = $plan->items
                ->where('priority', '<=', 20)
                ->whereIn('status', [DentalPlanItem::STATUS_ACCEPTED, DentalPlanItem::STATUS_PROPOSED]);
            foreach ($high->take(3) as $item) {
                $tooth = $item->tooth_code ? " tooth {$item->tooth_code}" : '';
                $alerts[] = [
                    'code' => 'dental.plan.high_priority',
                    'severity' => 'warning',
                    'message' => "High-priority planned: {$item->label}{$tooth}.",
                ];
            }
        }

        $overdue = DentalRecall::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('patient_id', $patient->id)
            ->whereIn('status', [DentalRecall::STATUS_SCHEDULED, DentalRecall::STATUS_DUE])
            ->where('due_on', '<', now()->toDateString())
            ->orderBy('due_on')
            ->limit(3)
            ->get();

        foreach ($overdue as $recall) {
            $reason = $recall->reason ?: 'Recall';
            $alerts[] = [
                'code' => 'dental.recall.overdue',
                'severity' => 'warning',
                'message' => "Overdue recall ({$reason}) due {$recall->due_on->format('d M Y')}.",
            ];
        }

        return $alerts;
    }
}
