<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\LegacyRevenueRepository;
use App\Repositories\RevenueRepository;

final class RevenueParityService
{
    private const TOLERANCE = 0.01;

    /** @var string */
    private $hospitalCode;
    /** @var RevenueRepository */
    private $current;
    /** @var LegacyRevenueRepository */
    private $legacy;

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

    /**
     * @return array<string, mixed>
     */
    public function run(int $year, int $compareYear, string $fromDate, string $toDate): array
    {
        $hospital = \config('hospitals.' . $this->hospitalCode, []);
        $mohCode = (string)($hospital['moh_customer_code'] ?? '');
        $companyId = (int)($hospital['profit_loss_company_id'] ?? 0);

        $yearFunctions = $this->runMode($year, $compareYear, null, null, $mohCode, $companyId);
        $rangeFunctions = $this->runMode($year, $compareYear, $fromDate, $toDate, $mohCode, $companyId);

        $all = array_merge($yearFunctions, $rangeFunctions);
        $failures = 0;
        foreach ($all as $item) {
            if (($item['status'] ?? 'FAIL') !== 'PASS') {
                $failures++;
            }
        }

        return [
            'schema_version' => '1.0',
            'phase' => '10.9',
            'hospital' => strtoupper($this->hospitalCode),
            'year' => $year,
            'compare_year' => $compareYear,
            'range_from' => $fromDate,
            'range_to' => $toDate,
            'compare_range_from' => RevenueRepository::shiftDateToYear($fromDate, $compareYear),
            'compare_range_to' => RevenueRepository::shiftDateToYear($toDate, $compareYear),
            'tolerance' => self::TOLERANCE,
            'write_operations' => 'NONE',
            'source_contract' => [
                'revenue_table' => 'T_INP_OUTP_REVENUE_BATCH',
                'revenue_month' => "TO_NUMBER(TO_CHAR(VISIT_END_DATE, 'MM'))",
                'total_expression' => 'NVL(CREDIT_VALUE,0)+NVL(CASH_VALUE,0)',
                'cash_expression' => 'NVL(CASH_VALUE,0)',
                'insurance_expression' => 'NVL(CREDIT_VALUE,0)',
                'moh_filter' => 'CUSTOMER_CODE=' . $mohCode,
                'profit_loss_table' => 'INCOME_STATMENTS',
                'profit_loss_company_id' => $companyId,
                'date_range_rule' => 'same-year current range compared with same month/day range in compare year',
                'profit_loss_range_rule' => 'inclusive selected month span because source is monthly statement data',
            ],
            'modes' => [
                'year' => ['functions' => $yearFunctions],
                'date_range' => ['functions' => $rangeFunctions],
            ],
            'functions_checked' => count($all),
            'failures' => $failures,
            'status' => $failures === 0 ? 'PASS' : 'FAIL',
        ];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function runMode(
        int $year,
        int $compareYear,
        ?string $fromDate,
        ?string $toDate,
        string $mohCode,
        int $companyId
    ): array {
        $definitions = [
            'monthly_revenue' => ['type' => 'total', 'customer' => null],
            'monthly_revenue_csh' => ['type' => 'cash', 'customer' => null],
            'monthly_revenue_ins' => ['type' => 'insurance', 'customer' => null],
            'monthly_revenue_moh' => ['type' => 'moh', 'customer' => $mohCode],
        ];

        $results = [];
        foreach ($definitions as $legacyFn => $def) {
            $legacyRows = $this->legacy->monthlyRevenue(
                $year,
                $compareYear,
                (string)$def['type'],
                $def['customer'] !== null ? (string)$def['customer'] : null,
                $fromDate,
                $toDate
            );
            $currentRows = $this->current->monthlyRevenue(
                $year,
                $compareYear,
                (string)$def['type'],
                $def['customer'] !== null ? (string)$def['customer'] : null,
                $fromDate,
                $toDate
            );
            $results[] = $this->compare($legacyFn, $legacyRows, $currentRows);
        }

        $legacyProfit = $this->legacy->monthlyProfitLoss($year, $compareYear, $companyId, $fromDate, $toDate);
        $currentProfit = $this->current->monthlyProfitLoss($year, $compareYear, $companyId, $fromDate, $toDate);
        $results[] = $this->compare('monthly_profilt_loss', $legacyProfit, $currentProfit);

        return $results;
    }

    /**
     * @param array<int, array<string, mixed>> $legacyRows
     * @param array<int, array<string, mixed>> $currentRows
     * @return array<string, mixed>
     */
    private function compare(string $legacyFunction, array $legacyRows, array $currentRows): array
    {
        $legacyByCode = [];
        foreach ($legacyRows as $row) {
            $legacyByCode[(int)($row['code'] ?? 0)] = $row;
        }
        $currentByCode = [];
        foreach ($currentRows as $row) {
            $currentByCode[(int)($row['code'] ?? 0)] = $row;
        }

        $codes = array_unique(array_merge(array_keys($legacyByCode), array_keys($currentByCode)));
        sort($codes);
        $monthResults = [];
        $maxDifference = 0.0;
        $legacyCurrentTotal = 0.0;
        $newCurrentTotal = 0.0;
        $legacyCompareTotal = 0.0;
        $newCompareTotal = 0.0;
        $ok = count($codes) > 0;

        foreach ($codes as $code) {
            $legacy = $legacyByCode[$code] ?? [];
            $current = $currentByCode[$code] ?? [];
            $legacyCurrent = (float)($legacy['current_value'] ?? 0);
            $newCurrent = (float)($current['current_value'] ?? 0);
            $legacyCompare = (float)($legacy['compare_value'] ?? 0);
            $newCompare = (float)($current['compare_value'] ?? 0);
            $currentDifference = abs($legacyCurrent - $newCurrent);
            $compareDifference = abs($legacyCompare - $newCompare);
            $maxDifference = max($maxDifference, $currentDifference, $compareDifference);
            if ($currentDifference > self::TOLERANCE || $compareDifference > self::TOLERANCE) {
                $ok = false;
            }
            $legacyCurrentTotal += $legacyCurrent;
            $newCurrentTotal += $newCurrent;
            $legacyCompareTotal += $legacyCompare;
            $newCompareTotal += $newCompare;
            $monthResults[] = [
                'month_code' => $code,
                'month_name' => (string)($current['month_name'] ?? $legacy['month_name'] ?? ''),
                'legacy_current' => $legacyCurrent,
                'new_current' => $newCurrent,
                'current_difference' => $currentDifference,
                'legacy_compare' => $legacyCompare,
                'new_compare' => $newCompare,
                'compare_difference' => $compareDifference,
                'status' => ($currentDifference <= self::TOLERANCE && $compareDifference <= self::TOLERANCE) ? 'PASS' : 'FAIL',
            ];
        }

        return [
            'legacy_function' => $legacyFunction,
            'legacy_rows' => count($legacyRows),
            'new_rows' => count($currentRows),
            'legacy_current_total' => $legacyCurrentTotal,
            'new_current_total' => $newCurrentTotal,
            'legacy_compare_total' => $legacyCompareTotal,
            'new_compare_total' => $newCompareTotal,
            'current_total_difference' => abs($legacyCurrentTotal - $newCurrentTotal),
            'compare_total_difference' => abs($legacyCompareTotal - $newCompareTotal),
            'max_month_difference' => $maxDifference,
            'months' => $monthResults,
            'status' => $ok ? 'PASS' : 'FAIL',
        ];
    }
}
