<?php

declare(strict_types=1);

namespace App\Repositories;

use App\Core\Database;
use DateTimeImmutable;

/**
 * Independent execution path for the old dashboard function popr_s.
 * It intentionally does not call OperationsRepository.
 */
final class LegacyOperationsRepository
{
    /** @var string */
    private $hospitalCode;

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

    /**
     * Legacy contract: get_patients_operations_s / popr_s.
     *
     * @return array<int, array<string, mixed>>
     */
    public function monthlyPatientOperations(
        int $year,
        int $compareYear,
        ?string $fromDate = null,
        ?string $toDate = null
    ): array {
        $current = $this->runLegacyPeriod($year, $fromDate, $toDate);
        if ($fromDate !== null && $toDate !== null) {
            $compareFrom = self::shiftDateToYear($fromDate, $compareYear);
            $compareTo = self::shiftDateToYear($toDate, $compareYear);
            $compare = $this->runLegacyPeriod($compareYear, $compareFrom, $compareTo);
        } else {
            $compare = $this->runLegacyPeriod($compareYear, null, null);
        }

        $currentMap = $this->monthMap($current);
        $compareMap = $this->monthMap($compare);
        $rows = [];
        $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',
        ];
        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(int $year, ?string $fromDate, ?string $toDate): array
    {
        $where = "EXTRACT(YEAR FROM r.expecteddonedate) = :legacy_year";
        $params = ['legacy_year' => $year];
        if ($fromDate !== null && $toDate !== null) {
            $where = "r.expecteddonedate >= TO_DATE(:legacy_from, 'YYYY-MM-DD')\n"
                   . "                  AND r.expecteddonedate < TO_DATE(:legacy_to, 'YYYY-MM-DD') + 1";
            $params = ['legacy_from' => $fromDate, 'legacy_to' => $toDate];
        }

        // Direct expression of the old get_patients_operations_s SQL contract.
        $sql = "
            SELECT
                TO_NUMBER(TO_CHAR(r.expecteddonedate, 'MM')) month_id,
                TO_CHAR(r.expecteddonedate, 'month') month_name,
                COUNT(1) value
            FROM rcp_service_reservation r,
                 oper_operation_schedule o
            WHERE r.ser = o.ser
              AND r.servstatus <> 'C'
              AND {$where}
            GROUP BY
                TO_CHAR(r.expecteddonedate, 'month'),
                TO_NUMBER(TO_CHAR(r.expecteddonedate, 'MM'))
            ORDER BY TO_NUMBER(TO_CHAR(r.expecteddonedate, '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_id'] ?? 0);
            if ($monthId < 1 || $monthId > 12) {
                continue;
            }
            $out[$monthId] = [
                'month_name' => trim((string)($row['month_name'] ?? '')),
                'value' => (float)($row['value'] ?? 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);
    }
}
