<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\LegacyPatientActivityRepository;
use App\Repositories\VisitsRepository;

final class AdmissionCategoryParityService
{
    /** @var VisitsRepository */
    private $current;

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

    /** @var string */
    private $hospitalCode;

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

    /** @return array<string, mixed> */
    public function run(int $year, string $fromDate, string $toDate): array
    {
        $yearResult = $this->compare(
            'year',
            $this->legacy->admissionCategoryMix($year),
            $this->current->admissionCategoryMix($year)
        );

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

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

        return [
            'schema_version' => '1.0',
            'phase' => 'H11.0-P3',
            'hospital' => strtoupper($this->hospitalCode),
            'year' => $year,
            'range_from' => $fromDate,
            'range_to' => $toDate,
            'legacy_function' => 'acty',
            'legacy_function_name' => 'get_admission_category_type',
            'source_evidence_boundary' => 'FULL_HISTORICAL_FUNCTION_BODY_RECOVERED',
            'source_contract' => [
                'oracle_sources' => ['RCP_VISIT', 'REG_COMP_CATEGORY', 'STP_PATCAT_TYPE'],
                'category_rule' => 'CAT_TYPE 1=Cash, 2=Credit, 3=Insurance',
                'join_rule' => 'RCP_VISIT joins REG_COMP_CATEGORY by HOSP_ID, FINANCLASSID, PATCATID, MAIN_PATCATID; PAT_CAT_TYPE maps to STP_PATCAT_TYPE.CAT_TYPE_ID',
                'branch_rule' => 'HOSP_ID IN (1) inside the active branch connection',
                'admission_rule' => 'CLASSCODE = 1',
                'year_rule' => 'EXTRACT(YEAR FROM VISIT_START_DATE) = selected year',
                'range_rule' => 'inclusive selected calendar-day window',
            ],
            'modes' => [
                'year' => ['function' => $yearResult],
                'date_range' => ['function' => $rangeResult],
            ],
            'functions_checked' => count($rows),
            'failures' => $failures,
            'write_operations' => 'NONE',
            'status' => $failures === 0 ? 'PASS' : 'FAIL',
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $legacyRows
     * @param array<int, array<string, mixed>> $currentRows
     * @return array<string, mixed>
     */
    private function compare(string $mode, array $legacyRows, array $currentRows): array
    {
        $legacy = $this->map($legacyRows);
        $current = $this->map($currentRows);
        $categories = array_values(array_unique(array_merge(array_keys($legacy), array_keys($current))));
        sort($categories);

        $details = [];
        $maxDifference = 0.0;
        $ok = true;

        foreach ($categories as $category) {
            $legacyValue = (float)($legacy[$category] ?? 0);
            $currentValue = (float)($current[$category] ?? 0);
            $difference = abs($legacyValue - $currentValue);
            if ($difference !== 0.0) {
                $ok = false;
            }
            $maxDifference = max($maxDifference, $difference);
            $details[] = [
                'category' => $category,
                'legacy_value' => $legacyValue,
                'current_value' => $currentValue,
                'difference' => $difference,
                'status' => $difference === 0.0 ? 'PASS' : 'FAIL',
            ];
        }

        foreach (['cash', 'credit', 'insurance'] as $requiredCategory) {
            if (!array_key_exists($requiredCategory, $legacy) || !array_key_exists($requiredCategory, $current)) {
                $ok = false;
            }
        }

        return [
            'mode' => $mode,
            'legacy_function' => 'acty',
            'comparison_type' => 'category_mix',
            'legacy_rows' => count($legacyRows),
            'current_rows' => count($currentRows),
            'legacy_total' => array_sum($legacy),
            'current_total' => array_sum($current),
            'total_difference' => abs(array_sum($legacy) - array_sum($current)),
            'max_difference' => $maxDifference,
            'categories' => $details,
            'status' => $ok ? 'PASS' : 'FAIL',
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $rows
     * @return array<string, float>
     */
    private function map(array $rows): array
    {
        $out = [
            'cash' => 0.0,
            'credit' => 0.0,
            'insurance' => 0.0,
        ];

        foreach ($rows as $row) {
            $category = strtolower(trim((string)($row['category'] ?? 'other')));
            if (!array_key_exists($category, $out)) {
                $category = 'other';
                if (!isset($out[$category])) {
                    $out[$category] = 0.0;
                }
            }
            $out[$category] += (float)($row['value'] ?? 0);
        }

        return $out;
    }
}
