<?php

declare(strict_types=1);

namespace App\Repositories;

use App\Core\Database;
use DateTimeImmutable;

/**
 * Independent execution path for old dashboard functions plts_s and prts_s.
 * It intentionally does not call DiagnosticsRepository.
 */
final class LegacyDiagnosticsRepository
{
    /** @var string */
    private $hospitalCode;

    public function __construct(string $hospitalCode)
    {
        $this->hospitalCode = strtolower($hospitalCode);
    }

    /** @return array<int, array<string, mixed>> */
    public function monthlyLabTests(
        int $year,
        int $compareYear,
        ?string $fromDate = null,
        ?string $toDate = null
    ): array {
        return $this->comparePeriods('lab', $year, $compareYear, $fromDate, $toDate);
    }

    /** @return array<int, array<string, mixed>> */
    public function monthlyRadiologyTests(
        int $year,
        int $compareYear,
        ?string $fromDate = null,
        ?string $toDate = null
    ): array {
        return $this->comparePeriods('radiology', $year, $compareYear, $fromDate, $toDate);
    }

    /** @return array<int, array<string, mixed>> */
    private function comparePeriods(
        string $kind,
        int $year,
        int $compareYear,
        ?string $fromDate,
        ?string $toDate
    ): array {
        $current = $this->runLegacyPeriod($kind, $year, $fromDate, $toDate);
        if ($fromDate !== null && $toDate !== null) {
            $compareFrom = self::shiftDateToYear($fromDate, $compareYear);
            $compareTo = self::shiftDateToYear($toDate, $compareYear);
            $compare = $this->runLegacyPeriod($kind, $compareYear, $compareFrom, $compareTo);
        } else {
            $compare = $this->runLegacyPeriod($kind, $compareYear, null, null);
        }

        $currentMap = $this->monthMap($current);
        $compareMap = $this->monthMap($compare);
        $months = [
            1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April',
            5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August',
            9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December',
        ];
        $rows = [];
        foreach ($months as $monthId => $fallbackName) {
            $rows[] = [
                'month_id' => $monthId,
                'month_name' => (string)($currentMap[$monthId]['month_name'] ?? $compareMap[$monthId]['month_name'] ?? $fallbackName),
                'current_value' => (float)($currentMap[$monthId]['value'] ?? 0),
                'compare_value' => (float)($compareMap[$monthId]['value'] ?? 0),
            ];
        }
        return $rows;
    }

    /** @return array<int, array<string, mixed>> */
    private function runLegacyPeriod(string $kind, int $year, ?string $fromDate, ?string $toDate): array
    {
        if ($kind === 'lab') {
            $table = 'Rcp_Lab_Service_Reservation';
            $dateExpr = 'rcp_lab_service_reservation.expecteddonedate';
        } elseif ($kind === 'radiology') {
            $table = 'RCP_RAD_SERVICE_RESERVATION';
            $dateExpr = 'RCP_RAD_SERVICE_RESERVATION.machine_sch_from';
        } else {
            throw new \InvalidArgumentException('Unknown legacy diagnostics contract.');
        }

        $where = "EXTRACT(YEAR FROM {$dateExpr}) = :legacy_year";
        $params = ['legacy_year' => $year];
        if ($fromDate !== null && $toDate !== null) {
            $where = "{$dateExpr} >= TO_DATE(:legacy_from, 'YYYY-MM-DD')\n"
                   . "                  AND {$dateExpr} < TO_DATE(:legacy_to, 'YYYY-MM-DD') + 1";
            $params = ['legacy_from' => $fromDate, 'legacy_to' => $toDate];
        }

        // Direct expression of legacy selected-branch functions get_lab_tests_s / get_rad_tests_s.
        $sql = "
            SELECT
                COUNT(1) AS month_wise_count,
                TO_CHAR({$dateExpr}, 'month') AS month_name,
                TO_NUMBER(TO_CHAR({$dateExpr}, 'mm')) month_no
            FROM {$table}
            WHERE {$dateExpr} <= SYSDATE
              AND {$where}
            GROUP BY
                TO_CHAR({$dateExpr}, 'month'),
                TO_NUMBER(TO_CHAR({$dateExpr}, 'mm'))
            ORDER BY TO_NUMBER(TO_CHAR({$dateExpr}, 'mm'))
        ";

        return Database::fetchAll($this->hospitalCode, $sql, $params);
    }

    /** @return array<int, array<string, mixed>> */
    private function monthMap(array $rows): array
    {
        $out = [];
        foreach ($rows as $row) {
            $monthId = (int)($row['month_no'] ?? 0);
            if ($monthId < 1 || $monthId > 12) {
                continue;
            }
            $out[$monthId] = [
                'month_name' => trim((string)($row['month_name'] ?? '')),
                'value' => (float)($row['month_wise_count'] ?? 0),
            ];
        }
        return $out;
    }

    private static function shiftDateToYear(string $date, int $targetYear): string
    {
        $source = DateTimeImmutable::createFromFormat('!Y-m-d', $date);
        if (!$source) {
            return sprintf('%04d-01-01', $targetYear);
        }
        $month = (int)$source->format('m');
        $day = (int)$source->format('d');
        while ($day >= 1) {
            $candidate = sprintf('%04d-%02d-%02d', $targetYear, $month, $day);
            $parsed = DateTimeImmutable::createFromFormat('!Y-m-d', $candidate);
            if ($parsed && $parsed->format('Y-m-d') === $candidate) {
                return $candidate;
            }
            $day--;
        }
        return sprintf('%04d-%02d-01', $targetYear, $month);
    }
}
