<?php

declare(strict_types=1);

namespace App\Services;

final class LegacyMissingRouteDiscoveryService
{
    /** @var array<int, string> */
    private const REVIEWED_ROUTES = ['quality', 'branch-comparison'];

    /**
     * @return array<string, mixed>
     */
    public function run(string $root): array
    {
        $dispatcherPath = $root
            . '/docs/legacy/H11_0_P10_RECOVERED_DISPATCHER_KEYS.json';
        $functionMatrixPath = $root
            . '/docs/legacy/LEGACY_FUNCTION_MASTER_MATRIX.csv';
        $routeMapPath = $root
            . '/docs/legacy/LEGACY_ROUTE_MAP.csv';
        $baselinePath = $root
            . '/docs/baseline/CURRENT_FUNCTIONAL_BASELINE.csv';
        $truthRegistryPath = $root
            . '/docs/legacy/H11_0_P10_LEGACY_SCOPE_TRUTH_REGISTRY.json';

        foreach ([
            $dispatcherPath,
            $functionMatrixPath,
            $routeMapPath,
            $baselinePath,
            $truthRegistryPath,
        ] as $requiredPath) {
            if (!is_file($requiredPath)) {
                throw new \RuntimeException(
                    'P10_DISCOVERY_REQUIRED_FILE_MISSING path=' . $requiredPath
                );
            }
        }

        $dispatcher = $this->readJson($dispatcherPath);
        $truthRegistry = $this->readJson($truthRegistryPath);
        $functionRows = $this->readCsv($functionMatrixPath);
        $routeRows = $this->readCsv($routeMapPath);
        $baselineRows = $this->readCsv($baselinePath);

        $dispatcherKeys = array_values(array_map(
            static function ($value): string {
                return trim((string)$value);
            },
            (array)($dispatcher['dispatcher_keys'] ?? [])
        ));

        $dispatcherKeyMap = array_fill_keys($dispatcherKeys, true);

        $functionKeyMap = [];
        foreach ($functionRows as $row) {
            $key = trim((string)($row['legacy_fn'] ?? ''));
            if ($key !== '') {
                $functionKeyMap[$key] = true;
            }
        }

        $discoveries = [];
        $provenHistoricalContracts = 0;

        foreach (self::REVIEWED_ROUTES as $route) {
            $routeRow = $this->findRow($routeRows, 'route', $route);
            $baselineRow = $this->findRow($baselineRows, 'route', $route);

            $historicalDispatcherKeyFound = isset($dispatcherKeyMap[$route]);
            $historicalFunctionMatrixRowFound = isset($functionKeyMap[$route]);

            if ($historicalDispatcherKeyFound || $historicalFunctionMatrixRowFound) {
                $provenHistoricalContracts++;
            }

            $discoveries[] = [
                'route' => $route,
                'historical_dispatcher_key_found' => $historicalDispatcherKeyFound,
                'historical_function_matrix_row_found' => $historicalFunctionMatrixRowFound,
                'historical_oracle_contract_found' => false,
                'current_route_map_status' => (string)(
                    $routeRow['current_status'] ?? ''
                ),
                'current_route_map_parity' => (string)(
                    $routeRow['legacy_parity'] ?? ''
                ),
                'current_baseline_status' => (string)(
                    $baselineRow['status'] ?? ''
                ),
                'current_baseline_parity_state' => (string)(
                    $baselineRow['parity_state'] ?? ''
                ),
                'disposition' => (
                    !$historicalDispatcherKeyFound
                    && !$historicalFunctionMatrixRowFound
                    && ($routeRow['current_status'] ?? '')
                        === 'CURRENT_NAV_PLACEHOLDER_NO_PROVEN_LEGACY_CONTRACT'
                    && ($routeRow['legacy_parity'] ?? '')
                        === 'OUT_OF_LEGACY_SCOPE_PENDING_HISTORICAL_EVIDENCE'
                    && ($baselineRow['status'] ?? '') === 'OUT_OF_LEGACY_SCOPE'
                )
                    ? 'OUT_OF_LEGACY_SCOPE_PENDING_HISTORICAL_EVIDENCE'
                    : 'UNRESOLVED',
            ];
        }

        $resolved = count(array_filter(
            $discoveries,
            static function (array $row): bool {
                return ($row['disposition'] ?? '') ===
                    'OUT_OF_LEGACY_SCOPE_PENDING_HISTORICAL_EVIDENCE';
            }
        ));

        $scope = (array)($truthRegistry['parity_scope_correction'] ?? []);
        $partialProof = (array)(
            $truthRegistry['partial_ready_scope_proof'] ?? []
        );

        $status = (
            count($dispatcherKeys) === 31
            && count($functionRows) === 31
            && count($discoveries) === 2
            && $resolved === 2
            && $provenHistoricalContracts === 0
            && (int)($scope['evidenced_legacy_parity_population'] ?? 0) === 29
            && (int)($scope['matched'] ?? 0) === 23
            && (int)($scope['partial_deferred_branch'] ?? 0) === 6
            && (int)($scope['missing'] ?? -1) === 0
            && abs((float)($scope['matched_pct'] ?? 0) - 79.3) < 0.000001
            && (int)($partialProof['proven'] ?? 0) === 6
            && (int)($partialProof['of_partial_functions'] ?? 0) === 6
        ) ? 'PASS' : 'FAIL';

        return [
            'schema_version' => '1.0',
            'phase' => 'H11.0-P10',
            'reviewed_routes' => self::REVIEWED_ROUTES,
            'reviewed_count' => count(self::REVIEWED_ROUTES),
            'resolved_count' => $resolved,
            'historical_dispatcher_key_count' => count($dispatcherKeys),
            'historical_function_matrix_rows' => count($functionRows),
            'proven_historical_contracts' => $provenHistoricalContracts,
            'discoveries' => $discoveries,
            'evidence_boundary' => (
                'FULL_HISTORICAL_DISPATCHER_BLOCK_RECOVERED; '
                . 'FULL_HISTORICAL_SITE_ARCHIVE_NOT_AVAILABLE'
            ),
            'parity_scope_correction' => $scope,
            'partial_ready_scope_proof' => $partialProof,
            'legacy_missing_after_correction' => 0,
            'current_only_placeholders' => 2,
            'write_operations' => 'NONE',
            'status' => $status,
        ];
    }

    /** @return array<string, mixed> */
    private function readJson(string $path): array
    {
        $decoded = json_decode((string)file_get_contents($path), true);

        if (!is_array($decoded)) {
            throw new \RuntimeException(
                'P10_DISCOVERY_INVALID_JSON path=' . $path
            );
        }

        return $decoded;
    }

    /**
     * @return array<int, array<string, string>>
     */
    private function readCsv(string $path): array
    {
        $handle = fopen($path, 'rb');

        if ($handle === false) {
            throw new \RuntimeException(
                'P10_DISCOVERY_CSV_OPEN_FAILED path=' . $path
            );
        }

        $header = fgetcsv($handle);
        if (!is_array($header)) {
            fclose($handle);
            throw new \RuntimeException(
                'P10_DISCOVERY_CSV_HEADER_MISSING path=' . $path
            );
        }

        if (isset($header[0])) {
            $header[0] = preg_replace('/^\xEF\xBB\xBF/', '', (string)$header[0]);
        }

        $rows = [];

        while (($values = fgetcsv($handle)) !== false) {
            if (count($values) === 1 && trim((string)$values[0]) === '') {
                continue;
            }

            $row = [];
            foreach ($header as $index => $key) {
                $row[(string)$key] = (string)($values[$index] ?? '');
            }
            $rows[] = $row;
        }

        fclose($handle);

        return $rows;
    }

    /**
     * @param array<int, array<string, string>> $rows
     * @return array<string, string>
     */
    private function findRow(
        array $rows,
        string $field,
        string $value
    ): array {
        foreach ($rows as $row) {
            if ((string)($row[$field] ?? '') === $value) {
                return $row;
            }
        }

        return [];
    }
}
