<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\BedsRepository;
use App\Repositories\LegacyBedsRepository;

final class BedsParityService
{
    /** @var LegacyBedsRepository */
    private $legacy;
    /** @var BedsRepository */
    private $current;

    public function __construct(string $hospitalCode)
    {
        $this->legacy = new LegacyBedsRepository($hospitalCode);
        $this->current = new BedsRepository($hospitalCode);
    }

    /** @return array<string,mixed> */
    public function run(int $year, string $fromDate, string $toDate): array
    {
        $snapshot = $this->compareSnapshot(
            'bcor',
            $this->legacy->currentOccupancyRows(),
            $this->current->legacyCompatibleCurrentOccupancyRows()
        );

        $legacyYearUnits = $this->legacy->unitEfficiencyRows($year, null, null, 0);
        $currentYearUnits = $this->current->legacyCompatibleUnitEfficiencyRows($year, null, null, 0);
        $yearUnits = $this->compareRows(
            'get_units_effic',
            'year',
            $legacyYearUnits,
            $currentYearUnits,
            ['bed_count','total_hours_occupied','total_hours_period','empty_hours','bed_efficiency_percentage'],
            static function (array $row): string {
                return (string)($row['unit_id'] ?? 0) . '|' . trim((string)($row['bed_floor'] ?? ''));
            }
        );

        $rangeUnits = $this->compareRows(
            'get_units_effic',
            'range',
            $this->legacy->unitEfficiencyRows($year, $fromDate, $toDate, 0),
            $this->current->legacyCompatibleUnitEfficiencyRows($year, $fromDate, $toDate, 0),
            ['bed_count','total_hours_occupied','total_hours_period','empty_hours','bed_efficiency_percentage'],
            static function (array $row): string {
                return (string)($row['unit_id'] ?? 0) . '|' . trim((string)($row['bed_floor'] ?? ''));
            }
        );

        $unitId = 0;
        foreach ($legacyYearUnits as $row) {
            $candidate = (int)($row['unit_id'] ?? 0);
            if ($candidate > 0) {
                $unitId = $candidate;
                break;
            }
        }
        if ($unitId <= 0) {
            foreach ($currentYearUnits as $row) {
                $candidate = (int)($row['unit_id'] ?? 0);
                if ($candidate > 0) {
                    $unitId = $candidate;
                    break;
                }
            }
        }

        $yearBeds = $this->compareRows(
            'get_units_effic_b',
            'year',
            $this->legacy->bedEfficiencyRows($year, $unitId),
            $this->current->legacyCompatibleBedEfficiencyRows($year, $unitId),
            ['total_hours_occupied','total_hours_period','bed_efficiency_percentage'],
            static function (array $row): string {
                return (string)($row['unit_id'] ?? 0) . '|' . trim((string)($row['bed_floor'] ?? '')) . '|' . trim((string)($row['bed_name'] ?? ''));
            }
        );

        $rangeBeds = $this->compareRows(
            'get_units_effic_b',
            'range',
            $this->legacy->bedEfficiencyRows($year, $unitId, $fromDate, $toDate),
            $this->current->legacyCompatibleBedEfficiencyRows($year, $unitId, $fromDate, $toDate),
            ['total_hours_occupied','total_hours_period','bed_efficiency_percentage'],
            static function (array $row): string {
                return (string)($row['unit_id'] ?? 0) . '|' . trim((string)($row['bed_floor'] ?? '')) . '|' . trim((string)($row['bed_name'] ?? ''));
            }
        );

        $checks = [$snapshot, $yearUnits, $rangeUnits, $yearBeds, $rangeBeds];
        $failures = count(array_filter($checks, static function (array $row): bool {
            return ($row['status'] ?? '') !== 'PASS';
        }));
        $maxDifference = 0.0;
        $identityMismatches = 0;
        foreach ($checks as $row) {
            $maxDifference = max($maxDifference, (float)($row['max_difference'] ?? 0));
            $identityMismatches += (int)($row['identity_mismatches'] ?? 0);
        }

        return [
            'status' => $failures === 0 ? 'PASS' : 'FAIL',
            'hospital_scope' => 'selected Oracle connection; legacy hospital id contract preserved as 1',
            'year' => $year,
            'from_date' => $fromDate,
            'to_date' => $toDate,
            'selected_unit_id' => $unitId,
            'functions_checked' => count($checks),
            'failures' => $failures,
            'identity_mismatches' => $identityMismatches,
            'max_difference' => $maxDifference,
            'write_operations' => 'NONE',
            'checks' => $checks,
        ];
    }

    /** @param array<int,array<string,mixed>> $legacy @param array<int,array<string,mixed>> $current */
    private function compareSnapshot(string $function, array $legacy, array $current): array
    {
        $legacyMap = [];
        $currentMap = [];
        foreach ($legacy as $row) { $legacyMap[strtolower((string)($row['status'] ?? ''))] = (float)($row['rate'] ?? 0); }
        foreach ($current as $row) { $currentMap[strtolower((string)($row['status'] ?? ''))] = (float)($row['rate'] ?? 0); }
        $keys = array_values(array_unique(array_merge(array_keys($legacyMap), array_keys($currentMap))));
        sort($keys);
        $max = 0.0;
        $identity = 0;
        foreach ($keys as $key) {
            if (!array_key_exists($key, $legacyMap) || !array_key_exists($key, $currentMap)) { $identity++; continue; }
            $max = max($max, abs($legacyMap[$key] - $currentMap[$key]));
        }
        return [
            'legacy_function' => $function,
            'mode' => 'snapshot',
            'comparison_type' => 'busy_empty_snapshot',
            'legacy_rows' => count($legacy),
            'new_rows' => count($current),
            'identity_mismatches' => $identity,
            'max_difference' => $max,
            'status' => count($legacy) === count($current) && $identity === 0 && $max <= 0.0001 ? 'PASS' : 'FAIL',
            'legacy_data' => $legacy,
            'new_data' => $current,
        ];
    }

    /**
     * @param array<int,array<string,mixed>> $legacy
     * @param array<int,array<string,mixed>> $current
     * @param array<int,string> $numericFields
     * @param callable(array<string,mixed>):string $identity
     * @return array<string,mixed>
     */
    private function compareRows(string $function, string $mode, array $legacy, array $current, array $numericFields, callable $identity): array
    {
        $count = max(count($legacy), count($current));
        $max = 0.0;
        $identityMismatches = 0;
        $details = [];
        for ($i = 0; $i < $count; $i++) {
            $l = $legacy[$i] ?? null;
            $n = $current[$i] ?? null;
            $lIdentity = is_array($l) ? $identity($l) : '';
            $nIdentity = is_array($n) ? $identity($n) : '';
            $identityMatch = $lIdentity !== '' && $lIdentity === $nIdentity;
            if (!$identityMatch) { $identityMismatches++; }
            $rowMax = 0.0;
            foreach ($numericFields as $field) {
                $diff = abs((float)(is_array($l) ? ($l[$field] ?? 0) : 0) - (float)(is_array($n) ? ($n[$field] ?? 0) : 0));
                $rowMax = max($rowMax, $diff);
                $max = max($max, $diff);
            }
            $details[] = [
                'position' => $i + 1,
                'legacy_identity' => $lIdentity,
                'new_identity' => $nIdentity,
                'identity_match' => $identityMatch,
                'max_difference' => $rowMax,
            ];
        }
        $status = count($legacy) === count($current) && $identityMismatches === 0 && $max <= 0.0001 ? 'PASS' : 'FAIL';
        return [
            'legacy_function' => $function,
            'mode' => $mode,
            'comparison_type' => $function === 'get_units_effic_b' ? 'bed_ranking' : 'unit_efficiency_ranking',
            'legacy_rows' => count($legacy),
            'new_rows' => count($current),
            'identity_mismatches' => $identityMismatches,
            'max_difference' => $max,
            'status' => $status,
            'row_comparison' => $details,
        ];
    }
}
