<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\LegacyMultiBranchPatientActivityRepository;
use RuntimeException;

final class MultiBranchOutpatientParityService
{
    /** @var MultiBranchPatientActivityService */
    private $current;

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

    public function __construct()
    {
        $this->current = new MultiBranchPatientActivityService();
        $this->legacy = new LegacyMultiBranchPatientActivityRepository();
    }

    /**
     * @return array<string, mixed>
     */
    public function run(int $year, string $fromDate, string $toDate): array
    {
        $activeBranches = BranchReadinessPolicy::blockingQaBranches();
        $deferredBranches = BranchReadinessPolicy::deferredBranches();

        $yearResult = $this->compare(
            'year',
            $this->legacy->monthlyOutpatientVisits($year),
            $this->current->monthlyOutpatientVisits($year),
            $activeBranches
        );

        $rangeResult = $this->compare(
            'date_range',
            $this->legacy->monthlyOutpatientVisits(
                $year,
                $fromDate,
                $toDate
            ),
            $this->current->monthlyOutpatientVisits(
                $year,
                $fromDate,
                $toDate
            ),
            $activeBranches
        );

        $rows = [$yearResult, $rangeResult];

        $failures = count(
            array_filter(
                $rows,
                static function (array $row): bool {
                    return ($row['status'] ?? 'FAIL') !== 'PASS';
                }
            )
        );

        $currentAccessPaths = $this->current->accessPaths();
        $legacyAccessPaths = $this->legacy->accessPaths();

        $this->assertDistinctBranchIdentities(
            $currentAccessPaths,
            $activeBranches,
            'current'
        );

        $this->assertDistinctBranchIdentities(
            $legacyAccessPaths,
            $activeBranches,
            'legacy'
        );

        return [
            'schema_version' => '2.0',
            'phase' => 'H11.0-P4.5',
            'legacy_function' => 'gmov',
            'legacy_function_name' => 'get_monthly_outpatient_visits',
            'source_evidence_boundary' => 'FULL_HISTORICAL_FUNCTION_BODY_RECOVERED',
            'branch_access_policy' => 'READY_BRANCHES_ONLY_JCKH_DEFERRED',
            'active_qa_branches' => $activeBranches,
            'deferred_branches' => $deferredBranches,
            'database_update_branches' => BranchReadinessPolicy::databaseUpdateBranches(),
            'current_access_paths' => $currentAccessPaths,
            'legacy_access_paths' => $legacyAccessPaths,
            'connectivity_diagnostics' => $this->connectivityDiagnostics(),
            'contract' => [
                'classcode' => 2,
                'emergency_exclusion' => false,
                'reservation_status_rule' => "SERVSTATUS NOT IN ('C','F')",
                'scheduled_rule' => 'SCHED_SERIAL IS NOT NULL',
                'ready_branch_rule' => 'NJCH and MNH are blocking QA branches',
                'deferred_branch_rule' => 'JCKH excluded from blocking QA until database parity readiness is approved',
            ],
            'modes' => [
                'year' => ['function' => $yearResult],
                'date_range' => ['function' => $rangeResult],
            ],
            'functions_checked' => count($rows),
            'failures' => $failures,
            'write_operations' => 'NONE',
            'legacy_promotion_status' => 'PARTIAL_DEFERRED_BRANCH',
            'matched_promotion' => false,
            'status' => $failures === 0 ? 'PASS' : 'FAIL',
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function connectivityDiagnostics(): array
    {
        return [
            'current' => $this->current->connectivityDiagnostics(),
            'legacy' => $this->legacy->connectivityDiagnostics(),
            'branch_readiness' => BranchReadinessPolicy::contract(),
            'write_operations' => 'NONE',
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $legacyRows
     * @param array<int, array<string, mixed>> $currentRows
     * @param array<int, string> $activeBranches
     * @return array<string, mixed>
     */
    private function compare(
        string $mode,
        array $legacyRows,
        array $currentRows,
        array $activeBranches
    ): array {
        $details = [];
        $maxDifference = 0.0;

        $ok = (
            count($legacyRows) === 12
            && count($currentRows) === 12
        );

        for ($month = 1; $month <= 12; $month++) {
            $legacy = $legacyRows[$month - 1] ?? [];
            $current = $currentRows[$month - 1] ?? [];

            foreach ($activeBranches as $branch) {
                $legacyValue = (float)($legacy[$branch] ?? 0);
                $currentValue = (float)($current[$branch] ?? 0);
                $difference = abs($legacyValue - $currentValue);

                $maxDifference = max($maxDifference, $difference);

                if ($difference !== 0.0) {
                    $ok = false;
                }

                $details[] = [
                    'month_id' => $month,
                    'branch' => $branch,
                    'legacy_value' => $legacyValue,
                    'current_value' => $currentValue,
                    'difference' => $difference,
                    'status' => $difference === 0.0 ? 'PASS' : 'FAIL',
                ];
            }
        }

        return [
            'mode' => $mode,
            'legacy_function' => 'gmov',
            'comparison_type' => '12_months_x_ready_branches',
            'active_branches' => $activeBranches,
            'deferred_branches' => BranchReadinessPolicy::deferredBranches(),
            'legacy_rows' => count($legacyRows),
            'current_rows' => count($currentRows),
            'value_checks' => count($details),
            'max_difference' => $maxDifference,
            'details' => $details,
            'status' => $ok ? 'PASS' : 'FAIL',
        ];
    }

    /**
     * @param array<string, array<string, mixed>> $paths
     * @param array<int, string> $activeBranches
     */
    private function assertDistinctBranchIdentities(
        array $paths,
        array $activeBranches,
        string $label
    ): void {
        $seen = [];

        foreach ($activeBranches as $branch) {
            $fingerprint = (string)(
                $paths[$branch]['identity']['fingerprint'] ?? ''
            );

            if ($fingerprint === '') {
                throw new RuntimeException(
                    'MISSING_BRANCH_IDENTITY_FINGERPRINT path=' . $label
                    . '; branch=' . strtoupper($branch)
                );
            }

            if (isset($seen[$fingerprint])) {
                throw new RuntimeException(
                    'CROSS_BRANCH_IDENTITY_COLLISION path=' . $label
                    . '; branches=' . strtoupper($seen[$fingerprint])
                    . ',' . strtoupper($branch)
                );
            }

            $seen[$fingerprint] = $branch;
        }
    }
}
