<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\DoctorsRepository;
use App\Repositories\LegacyDoctorsRepository;

final class DoctorsParityService
{
    /** @var string */
    private $hospitalCode;

    /** @var LegacyDoctorsRepository */
    private $legacy;

    /** @var DoctorsRepository */
    private $current;

    public function __construct(string $hospitalCode)
    {
        $this->hospitalCode = strtolower($hospitalCode);
        $this->legacy = new LegacyDoctorsRepository($this->hospitalCode);
        $this->current = new DoctorsRepository($this->hospitalCode);
    }

    /** @return array<string, mixed> */
    public function run(int $year, int $compareYear, int $rankingLimit = 10): array
    {
        $periods = [
            'current_year' => $year,
            'compare_year' => $compareYear,
        ];

        $modes = [];
        $all = [];
        foreach ($periods as $mode => $periodYear) {
            $functions = [
                $this->compareScalar(
                    'total_op_visits',
                    $periodYear,
                    $this->legacy->totalOutpatientVisits($periodYear),
                    $this->current->annualMetricTotal($periodYear, 'TOTAL_OP_VISITS')
                ),
                $this->compareScalar(
                    'total_admission',
                    $periodYear,
                    $this->legacy->totalAdmissions($periodYear),
                    $this->current->annualMetricTotal($periodYear, 'TOTAL_INPAT_VISITS')
                ),
                $this->compareRanking(
                    'get_doc_rev',
                    $periodYear,
                    $this->legacy->doctorRevenueRanking($periodYear, $rankingLimit),
                    $this->current->topDoctors($periodYear, 'TOT_REV', $rankingLimit)
                ),
            ];
            $modes[$mode] = ['year' => $periodYear, 'functions' => $functions];
            foreach ($functions as $row) {
                $all[] = $row;
            }
        }

        $failures = count(array_filter($all, static function (array $row): bool {
            return ($row['status'] ?? 'FAIL') !== 'PASS';
        }));
        $maxDifference = 0.0;
        foreach ($all as $row) {
            $maxDifference = max($maxDifference, (float)($row['max_difference'] ?? 0));
        }

        return [
            'schema_version' => '1.0',
            'phase' => '10.13',
            'hospital' => strtoupper($this->hospitalCode),
            'year' => $year,
            'compare_year' => $compareYear,
            'ranking_limit' => $rankingLimit,
            'write_operations' => 'NONE',
            'source_contract' => [
                'get_doc_rev' => 'KPI_DOCTOR_PERF_DETAIL_JOB; YEARID; doctor-level TOT_REV ranking; top-N limit.',
                'total_op_visits' => 'KPI_DOCTOR_PERF_DETAIL_JOB; YEARID; doctor-level TOTAL_OP_VISITS total.',
                'total_admission' => 'KPI_DOCTOR_PERF_DETAIL_JOB; YEARID; doctor-level TOTAL_INPAT_VISITS total.',
                'aggregation_note' => 'Doctor-level values are deduplicated per doctor before final aggregation, matching the established dashboard doctor KPI contract.',
                'finance_note' => 'moh_revn, drug_rev and net_rev remain reserved for Phase 10.15 Specialty Finance recovery.',
            ],
            'modes' => $modes,
            'functions_checked' => count($all),
            'max_difference' => $maxDifference,
            'failures' => $failures,
            'status' => $failures === 0 && $maxDifference === 0.0 ? 'PASS' : 'FAIL',
        ];
    }

    /** @return array<string, mixed> */
    private function compareScalar(string $legacyFunction, int $year, float $legacyValue, float $newValue): array
    {
        $difference = abs($legacyValue - $newValue);
        return [
            'legacy_function' => $legacyFunction,
            'comparison_type' => 'scalar_annual_total',
            'year' => $year,
            'legacy_value' => $legacyValue,
            'new_value' => $newValue,
            'difference' => $difference,
            'max_difference' => $difference,
            'status' => $difference === 0.0 ? 'PASS' : 'FAIL',
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $legacyRows
     * @param array<int, array<string, mixed>> $currentRows
     * @return array<string, mixed>
     */
    private function compareRanking(string $legacyFunction, int $year, array $legacyRows, array $currentRows): array
    {
        $limit = max(count($legacyRows), count($currentRows));
        $details = [];
        $maxDifference = 0.0;
        $ok = count($legacyRows) === count($currentRows);

        for ($i = 0; $i < $limit; $i++) {
            $legacy = $legacyRows[$i] ?? [];
            $current = $currentRows[$i] ?? [];
            $legacyId = $this->rankingIdentity($legacy);
            $newId = $this->rankingIdentity($current);
            $legacyValue = (float)($legacy['value'] ?? 0);
            $newValue = (float)($current['value'] ?? 0);
            $difference = abs($legacyValue - $newValue);
            $identityMatch = $legacyId === $newId;
            $rowOk = $identityMatch && $difference === 0.0;
            if (!$rowOk) {
                $ok = false;
            }
            $maxDifference = max($maxDifference, $difference);
            $details[] = [
                'rank' => $i + 1,
                'legacy_identity' => $legacyId,
                'new_identity' => $newId,
                'identity_match' => $identityMatch,
                'legacy_value' => $legacyValue,
                'new_value' => $newValue,
                'difference' => $difference,
                'status' => $rowOk ? 'PASS' : 'FAIL',
            ];
        }

        return [
            'legacy_function' => $legacyFunction,
            'comparison_type' => 'top_n_doctor_ranking',
            'year' => $year,
            'legacy_rows' => count($legacyRows),
            'new_rows' => count($currentRows),
            'max_difference' => $maxDifference,
            'ranking_rows' => $details,
            'status' => $ok ? 'PASS' : 'FAIL',
        ];
    }

    /** @param array<string, mixed> $row */
    private function rankingIdentity(array $row): string
    {
        $id = trim((string)($row['id'] ?? ''));
        if ($id !== '') {
            return strtoupper($id);
        }
        return strtoupper(trim((string)($row['label'] ?? '')));
    }
}
