<?php

namespace App\Services\Care;

use App\Models\ClinicalPathway;
use App\Models\Consultation;
use App\Models\Member;
use App\Models\Patient;
use App\Models\PatientPathway;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\HttpException;

class PathwayService
{
    public function __construct(
        protected PathwayMatcher $matcher,
        protected AssessmentService $assessments,
        protected CarePermissions $permissions,
    ) {}

    /**
     * @param  iterable<\App\Models\Diagnosis|array{code?: string, description?: string}>  $diagnoses
     * @return Collection<int, array{pathway: ClinicalPathway, pathway_code: string, pathway_name: string, rank: int, match_reason: string, already_active: bool}>
     */
    public function suggest(Patient $patient, iterable $diagnoses): Collection
    {
        $activeIds = $this->activeFor($patient)->pluck('pathway_id')->all();

        return $this->matcher->match($diagnoses)->map(function (array $row) use ($activeIds) {
            $row['already_active'] = in_array($row['pathway']->id, $activeIds, true);

            return $row;
        });
    }

    /**
     * @param  array{
     *     consultation?: ?Consultation,
     *     activation_diagnosis_text?: ?string,
     *     actor?: ?string,
     *     notes?: ?string,
     * }  $context
     */
    public function activate(
        Patient $patient,
        ClinicalPathway $pathway,
        string $ownerRef,
        ?Member $member,
        array $context = [],
    ): PatientPathway {
        if (! $this->permissions->can($member, 'pathways.manage')) {
            throw new HttpException(403, 'Not authorized to manage clinical pathways.');
        }

        if (! $pathway->is_active) {
            throw ValidationException::withMessages([
                'pathway_code' => 'Pathway is inactive.',
            ]);
        }

        $consultation = $context['consultation'] ?? null;
        $actor = $context['actor'] ?? null;
        $diagnosisText = $context['activation_diagnosis_text']
            ?? $this->snapshotDiagnosisText($consultation);

        return DB::transaction(function () use ($patient, $pathway, $ownerRef, $member, $consultation, $actor, $diagnosisText, $context) {
            $existingActive = PatientPathway::query()
                ->where('patient_id', $patient->id)
                ->where('pathway_id', $pathway->id)
                ->where('status', PatientPathway::STATUS_ACTIVE)
                ->lockForUpdate()
                ->first();

            if ($existingActive) {
                return $existingActive->load(['pathway.templates', 'assessments.template']);
            }

            $patientPathway = PatientPathway::create([
                'owner_ref' => $ownerRef,
                'organization_id' => $patient->organization_id,
                'patient_id' => $patient->id,
                'pathway_id' => $pathway->id,
                'status' => PatientPathway::STATUS_ACTIVE,
                'activated_at' => now(),
                'activated_by' => $actor,
                'activation_consultation_id' => $consultation?->id,
                'activation_diagnosis_text' => $diagnosisText ? mb_substr($diagnosisText, 0, 500) : null,
                'notes' => $context['notes'] ?? null,
            ]);

            $pathway->loadMissing('templates');
            foreach ($pathway->templates->where('is_required_on_activation', true) as $binding) {
                try {
                    $this->assessments->start(
                        $patient,
                        $binding->template_code,
                        $ownerRef,
                        $member,
                        [
                            'consultation' => $consultation,
                            'visit' => $consultation?->visit,
                            'patient_pathway' => $patientPathway,
                            'actor' => $actor,
                        ],
                    );
                } catch (ValidationException $e) {
                    // Template may not be seeded yet (content pack PR); skip missing packs.
                    if (isset($e->errors()['template_code'])) {
                        continue;
                    }
                    throw $e;
                }
            }

            AuditLogger::record(
                $ownerRef,
                'pathway.activated',
                $patient->organization_id,
                $actor,
                PatientPathway::class,
                $patientPathway->id,
                [
                    'pathway_code' => $pathway->code,
                    'activation_diagnosis_text' => $patientPathway->activation_diagnosis_text,
                ],
            );

            return $patientPathway->load(['pathway.templates', 'assessments.template']);
        });
    }

    public function deactivate(
        PatientPathway $patientPathway,
        string $ownerRef,
        ?Member $member,
        ?string $actorRef = null,
    ): PatientPathway {
        if (! $this->permissions->can($member, 'pathways.manage')) {
            throw new HttpException(403, 'Not authorized to manage clinical pathways.');
        }

        if ($patientPathway->owner_ref !== $ownerRef) {
            throw new HttpException(404, 'Pathway enrollment not found.');
        }

        if ($patientPathway->status !== PatientPathway::STATUS_ACTIVE) {
            return $patientPathway;
        }

        $patientPathway->update([
            'status' => PatientPathway::STATUS_INACTIVE,
            'resolved_at' => now(),
        ]);

        AuditLogger::record(
            $ownerRef,
            'pathway.deactivated',
            $patientPathway->organization_id,
            $actorRef,
            PatientPathway::class,
            $patientPathway->id,
            ['pathway_code' => $patientPathway->pathway?->code ?? $patientPathway->pathway_id],
        );

        return $patientPathway->fresh(['pathway']);
    }

    /**
     * @return Collection<int, PatientPathway>
     */
    public function activeFor(Patient $patient): Collection
    {
        return PatientPathway::query()
            ->where('patient_id', $patient->id)
            ->where('status', PatientPathway::STATUS_ACTIVE)
            ->with('pathway')
            ->orderByDesc('activated_at')
            ->get();
    }

    protected function snapshotDiagnosisText(?Consultation $consultation): ?string
    {
        if (! $consultation) {
            return null;
        }

        $consultation->loadMissing('diagnoses');
        if ($consultation->diagnoses->isEmpty()) {
            return null;
        }

        return $consultation->diagnoses
            ->map(fn ($d) => trim(($d->code ? $d->code.' ' : '').$d->description))
            ->filter()
            ->implode('; ');
    }
}
