<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\LegacyOperationsRepository;
use App\Repositories\LegacyPatientActivityRepository;
use App\Repositories\OperationsRepository;
use App\Repositories\VisitsRepository;

final class LegacyBatchAReadyScopeParityService
{
    /** @var array<int, string> */
    private const FUNCTIONS = ['padm', 'perv', 'popr'];

    /**
     * Exact READY-scope proof for three historical three-branch composite functions.
     *
     * JCKH remains DEFERRED_NOT_READY, so these functions cannot be promoted to
     * MATCHED yet. A zero-difference PASS proves NJCH + MNH exactly and keeps the
     * status PARTIAL_DEFERRED_BRANCH until JCKH becomes READY.
     *
     * @return array<string, mixed>
     */
    public function run(
        int $year,
        int $compareYear,
        string $fromDate,
        string $toDate
    ): array {
        $branches = BranchReadinessPolicy::blockingQaBranches();
        $deferredBranches = BranchReadinessPolicy::deferredBranches();
        $modes = ['year', 'range'];

        $datasets = [];
        $details = [];
        $functionResults = [];
        $maxDifference = 0.0;
        $failedDatasets = 0;
        $identityMismatches = 0;

        foreach (self::FUNCTIONS as $functionKey) {
            $functionDatasetCount = 0;
            $functionValueChecks = 0;
            $functionFailures = 0;
            $functionMaxDifference = 0.0;

            foreach ($branches as $branch) {
                foreach ($modes as $mode) {
                    $rangeFrom = $mode === 'range' ? $fromDate : null;
                    $rangeTo = $mode === 'range' ? $toDate : null;

                    $pair = $this->executeFunction(
                        $functionKey,
                        $branch,
                        $year,
                        $compareYear,
                        $rangeFrom,
                        $rangeTo
                    );

                    $comparison = $this->compareMonthlyDataset(
                        $functionKey,
                        $branch,
                        $mode,
                        $pair['legacy'],
                        $pair['current']
                    );

                    $datasets[] = $comparison;
                    $functionDatasetCount++;
                    $functionValueChecks += (int)$comparison['value_checks'];
                    $functionMaxDifference = max(
                        $functionMaxDifference,
                        (float)$comparison['max_difference']
                    );
                    $maxDifference = max(
                        $maxDifference,
                        (float)$comparison['max_difference']
                    );
                    $identityMismatches += (int)$comparison['identity_mismatches'];

                    if (($comparison['status'] ?? 'FAIL') !== 'PASS') {
                        $failedDatasets++;
                        $functionFailures++;
                    }

                    foreach ((array)$comparison['details'] as $detail) {
                        $details[] = $detail;
                    }
                }
            }

            $functionResults[$functionKey] = [
                'status' => (
                    $functionDatasetCount === 4
                    && $functionFailures === 0
                    && $functionValueChecks === 96
                    && $functionMaxDifference === 0.0
                ) ? 'PASS' : 'FAIL',
                'datasets' => $functionDatasetCount,
                'value_checks' => $functionValueChecks,
                'failures' => $functionFailures,
                'max_difference' => $functionMaxDifference,
                'promotion_status' => 'PARTIAL_DEFERRED_BRANCH',
                'matched_promotion' => false,
            ];
        }

        $valueChecks = count($details);
        $status = (
            count($branches) === 2
            && count(self::FUNCTIONS) === 3
            && count($datasets) === 12
            && $valueChecks === 288
            && $failedDatasets === 0
            && $identityMismatches === 0
            && $maxDifference === 0.0
            && $this->allFunctionsPass($functionResults)
        ) ? 'PASS' : 'FAIL';

        return [
            'schema_version' => '1.0',
            'phase' => 'H11.0-P8',
            'batch' => 'Legacy Batch A',
            'functions' => self::FUNCTIONS,
            'function_count' => count(self::FUNCTIONS),
            'historical_contracts' => $this->historicalContracts(),
            'source_evidence_boundary' => 'FULL_HISTORICAL_FUNCTION_BODIES_RECOVERED',
            'branch_readiness_policy' => 'READY_BRANCHES_ONLY_JCKH_DEFERRED',
            'active_qa_branches' => $branches,
            'deferred_branches' => $deferredBranches,
            'database_update_branches' => BranchReadinessPolicy::databaseUpdateBranches(),
            'modes' => $modes,
            'modes_checked' => count($modes),
            'dataset_checks' => count($datasets),
            'value_checks' => $valueChecks,
            'identity_mismatches' => $identityMismatches,
            'failed_datasets' => $failedDatasets,
            'max_difference' => $maxDifference,
            'function_results' => $functionResults,
            'datasets' => $datasets,
            'details' => $details,
            'write_operations' => 'NONE',
            'matched_promotions' => 0,
            'ready_scope_contracts_proven' => $status === 'PASS' ? 3 : 0,
            'promotion_policy' => 'NO_MATCHED_PROMOTION_WHILE_JCKH_DEFERRED_FOR_INHERENT_THREE_BRANCH_COMPOSITES',
            'legacy_promotion_status' => $status === 'PASS'
                ? 'THREE_FUNCTIONS_READY_SCOPE_PROVEN_PARTIAL_DEFERRED_BRANCH'
                : 'NOT_PROVEN',
            'partial_ready_scope_proven_after_pass' => [
                'count' => $status === 'PASS' ? 4 : 1,
                'of_partial_functions' => 6,
                'functions' => $status === 'PASS'
                    ? ['gmov', 'padm', 'perv', 'popr']
                    : ['gmov'],
            ],
            'remaining_partial_ready_scope_unproven_after_pass' => $status === 'PASS'
                ? ['plts', 'prts']
                : ['padm', 'perv', 'popr', 'plts', 'prts'],
            'baseline_before' => [
                'known' => 31,
                'matched' => 23,
                'partial' => 6,
                'missing' => 2,
                'matched_pct' => 74.2,
            ],
            'baseline_after_live_pass' => [
                'known' => 31,
                'matched' => 23,
                'partial' => 6,
                'missing' => 2,
                'matched_pct' => 74.2,
            ],
            'status' => $status,
        ];
    }

    /**
     * @return array{legacy: array<int, array<string, mixed>>, current: array<int, array<string, mixed>>}
     */
    private function executeFunction(
        string $functionKey,
        string $branch,
        int $year,
        int $compareYear,
        ?string $fromDate,
        ?string $toDate
    ): array {
        if ($functionKey === 'padm') {
            $legacy = (new LegacyPatientActivityRepository($branch))->monthlyAdmissions(
                $year,
                $compareYear,
                $fromDate,
                $toDate
            );
            $current = (new VisitsRepository($branch))->monthlyAdmissions(
                $year,
                $compareYear,
                $fromDate,
                $toDate
            );

            return ['legacy' => $legacy, 'current' => $current];
        }

        if ($functionKey === 'perv') {
            $legacy = (new LegacyPatientActivityRepository($branch))->monthlyEmergencyVisits(
                $year,
                $compareYear,
                $fromDate,
                $toDate
            );
            $current = (new VisitsRepository($branch))->monthlyEmergencyVisits(
                $year,
                $compareYear,
                $fromDate,
                $toDate
            );

            return ['legacy' => $legacy, 'current' => $current];
        }

        if ($functionKey === 'popr') {
            $legacy = (new LegacyOperationsRepository($branch))->monthlyPatientOperations(
                $year,
                $compareYear,
                $fromDate,
                $toDate
            );
            $current = (new OperationsRepository($branch))->monthlyLegacyOperations(
                $year,
                $compareYear,
                $fromDate,
                $toDate
            );

            return ['legacy' => $legacy, 'current' => $current];
        }

        throw new \InvalidArgumentException('Unknown H11.0-P8 function: ' . $functionKey);
    }

    /**
     * @param array<int, array<string, mixed>> $legacyRows
     * @param array<int, array<string, mixed>> $currentRows
     * @return array<string, mixed>
     */
    private function compareMonthlyDataset(
        string $functionKey,
        string $branch,
        string $mode,
        array $legacyRows,
        array $currentRows
    ): array {
        $legacyMap = $this->monthMap($legacyRows);
        $currentMap = $this->monthMap($currentRows);
        $details = [];
        $maxDifference = 0.0;
        $identityMismatches = 0;
        $failures = 0;

        for ($month = 1; $month <= 12; $month++) {
            $legacyExists = array_key_exists($month, $legacyMap);
            $currentExists = array_key_exists($month, $currentMap);

            if (!$legacyExists || !$currentExists) {
                $identityMismatches++;
            }

            foreach (['current_value', 'compare_value'] as $series) {
                $legacyValue = $legacyExists
                    ? (float)($legacyMap[$month][$series] ?? 0)
                    : 0.0;
                $currentValue = $currentExists
                    ? (float)($currentMap[$month][$series] ?? 0)
                    : 0.0;
                $difference = abs($legacyValue - $currentValue);
                $rowPass = $legacyExists && $currentExists && $difference === 0.0;

                if (!$rowPass) {
                    $failures++;
                }

                $maxDifference = max($maxDifference, $difference);
                $details[] = [
                    'function' => $functionKey,
                    'branch' => $branch,
                    'mode' => $mode,
                    'month_id' => $month,
                    'month_name' => (string)(
                        $currentMap[$month]['month_name']
                        ?? $legacyMap[$month]['month_name']
                        ?? ''
                    ),
                    'series' => $series,
                    'legacy_exists' => $legacyExists,
                    'current_exists' => $currentExists,
                    'legacy_value' => $legacyValue,
                    'current_value' => $currentValue,
                    'difference' => $difference,
                    'status' => $rowPass ? 'PASS' : 'FAIL',
                ];
            }
        }

        return [
            'function' => $functionKey,
            'branch' => $branch,
            'mode' => $mode,
            'legacy_rows' => count($legacyRows),
            'current_rows' => count($currentRows),
            'value_checks' => count($details),
            'identity_mismatches' => $identityMismatches,
            'failures' => $failures,
            'max_difference' => $maxDifference,
            'details' => $details,
            'status' => (
                count($legacyRows) === 12
                && count($currentRows) === 12
                && count($details) === 24
                && $identityMismatches === 0
                && $failures === 0
                && $maxDifference === 0.0
            ) ? 'PASS' : 'FAIL',
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $rows
     * @return array<int, array<string, mixed>>
     */
    private function monthMap(array $rows): array
    {
        $map = [];

        foreach ($rows as $row) {
            $month = (int)($row['month_id'] ?? 0);

            if ($month >= 1 && $month <= 12) {
                $map[$month] = $row;
            }
        }

        return $map;
    }

    /** @param array<string, array<string, mixed>> $results */
    private function allFunctionsPass(array $results): bool
    {
        foreach (self::FUNCTIONS as $functionKey) {
            if (($results[$functionKey]['status'] ?? 'FAIL') !== 'PASS') {
                return false;
            }
        }

        return true;
    }

    /** @return array<string, array<string, mixed>> */
    private function historicalContracts(): array
    {
        return [
            'padm' => [
                'legacy_function' => 'get_patients_admissions',
                'sources' => ['RCP_ENTRYEXIT', 'RCP_VISIT', 'SEC_LOOKUP_DETAIL'],
                'date_field' => 'RCP_VISIT.VISIT_START_DATE',
                'rules' => [
                    'RCP_VISIT.HOSP_ID = RCP_ENTRYEXIT.BRANCHID',
                    'RCP_VISIT.PATIENTID = RCP_ENTRYEXIT.PATIENTID',
                    'RCP_VISIT.VISIT_ID = RCP_ENTRYEXIT.VISIT_ID',
                    'RCP_VISIT.HOSP_ID = 1',
                    'NVL(RCP_ENTRYEXIT.BABY_NORMAL_BED, 1) = 1',
                ],
            ],
            'perv' => [
                'legacy_function' => 'get_emergency_visits',
                'sources' => ['RCP_VISIT', 'RCP_SERVICE_RESERVATION', 'SEC_LOOKUP_DETAIL'],
                'date_field' => 'RCP_SERVICE_RESERVATION.EXPECTEDDONEDATE',
                'rules' => [
                    'RCP_VISIT.CLASSCODE = 2',
                    'RCP_SERVICE_RESERVATION.SERVSTATUS NOT IN (C,F)',
                    'RCP_SERVICE_RESERVATION.SCHED_SERIAL IS NOT NULL',
                    'NVL(OUTPTA_EMERGENCY_TYPE,0) = 1 OR CLINIC_ID IN (16,297,298)',
                ],
            ],
            'popr' => [
                'legacy_function' => 'get_patients_operations',
                'sources' => ['RCP_SERVICE_RESERVATION', 'OPER_OPERATION_SCHEDULE', 'SEC_LOOKUP_DETAIL'],
                'date_field' => 'RCP_SERVICE_RESERVATION.EXPECTEDDONEDATE',
                'rules' => [
                    'RCP_SERVICE_RESERVATION.SER = OPER_OPERATION_SCHEDULE.SER',
                    "RCP_SERVICE_RESERVATION.SERVSTATUS <> 'C'",
                ],
            ],
        ];
    }
}
