<?php

namespace App\Services\Care;

use App\Models\ClinicalPathway;
use App\Models\Diagnosis;
use Illuminate\Support\Collection;

/**
 * Match persisted diagnosis rows to clinical pathways (v1 algorithm).
 */
class PathwayMatcher
{
    /**
     * @param  iterable<Diagnosis|array{code?: ?string, description?: ?string}>  $diagnoses
     * @return Collection<int, array{
     *     pathway: ClinicalPathway,
     *     pathway_code: string,
     *     pathway_name: string,
     *     rank: int,
     *     match_reason: string
     * }>
     */
    public function match(iterable $diagnoses): Collection
    {
        $dxList = collect($diagnoses)->map(function ($d) {
            if ($d instanceof Diagnosis) {
                return [
                    'code' => $d->code,
                    'description' => $d->description,
                ];
            }

            return [
                'code' => $d['code'] ?? null,
                'description' => $d['description'] ?? null,
            ];
        })->filter(fn ($d) => filled($d['code']) || filled($d['description']));

        if ($dxList->isEmpty()) {
            return collect();
        }

        $pathways = ClinicalPathway::query()
            ->active()
            ->orderBy('sort_order')
            ->get();

        $matches = [];

        foreach ($pathways as $pathway) {
            $rules = $pathway->match_rules ?? [];
            $bestRank = null;
            $bestReason = null;

            foreach ($dxList as $dx) {
                $result = $this->matchDiagnosis($dx, $rules);
                if ($result === null) {
                    continue;
                }
                if ($bestRank === null || $result['rank'] > $bestRank) {
                    $bestRank = $result['rank'];
                    $bestReason = $result['reason'];
                }
            }

            if ($bestRank !== null) {
                $matches[] = [
                    'pathway' => $pathway,
                    'pathway_code' => $pathway->code,
                    'pathway_name' => $pathway->name,
                    'rank' => $bestRank,
                    'match_reason' => $bestReason,
                ];
            }
        }

        return collect($matches)
            ->sort(function ($a, $b) {
                if ($a['rank'] !== $b['rank']) {
                    return $b['rank'] <=> $a['rank'];
                }

                return $a['pathway']->sort_order <=> $b['pathway']->sort_order;
            })
            ->values();
    }

    /**
     * @param  array{code?: ?string, description?: ?string}  $dx
     * @param  array<string, mixed>  $rules
     * @return array{rank: int, reason: string}|null
     */
    protected function matchDiagnosis(array $dx, array $rules): ?array
    {
        $desc = $this->normalize((string) ($dx['description'] ?? ''));
        $codeStripped = $this->stripCode((string) ($dx['code'] ?? ''));

        foreach ($rules['exclude_keywords'] ?? [] as $exclude) {
            $ex = $this->normalize((string) $exclude);
            if ($ex !== '' && $desc !== '' && str_contains($desc, $ex)) {
                return null;
            }
        }

        foreach ($rules['icd_prefixes'] ?? [] as $prefix) {
            $p = $this->stripCode((string) $prefix);
            if ($p !== '' && $codeStripped !== '' && str_starts_with($codeStripped, $p)) {
                return ['rank' => 100, 'reason' => 'icd_prefix:'.$prefix];
            }
        }

        foreach ($rules['keywords'] ?? [] as $keyword) {
            $kw = $this->normalize((string) $keyword);
            if ($kw !== '' && $desc !== '' && str_contains($desc, $kw)) {
                return ['rank' => 50, 'reason' => 'keyword:'.$keyword];
            }
        }

        return null;
    }

    protected function normalize(string $s): string
    {
        $s = mb_strtolower(trim($s));
        $s = preg_replace('/\s+/u', ' ', $s) ?? $s;

        return $s;
    }

    protected function stripCode(string $code): string
    {
        return strtoupper(preg_replace('/[^A-Za-z0-9]/', '', $code) ?? '');
    }
}
