<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Request;
use App\Core\View;
use App\Repositories\DiagnosticsRepository;
use App\Services\ChartDataService;
use DateTimeImmutable;
use Throwable;

final class LabRadiologyController
{
    public function index(): void
    {
        Auth::requireLogin();

        $hospitalCode = Auth::hospital();
        $hospital = \config('hospitals.' . $hospitalCode, []);
        $currentYear = (int)date('Y');
        $yearWasRequested = Request::get('year') !== null;
        $compareYearWasRequested = Request::get('compare_year') !== null;
        $year = \safe_int(Request::get('year'), $currentYear);
        $compareYear = \safe_int(Request::get('compare_year'), $year - 1);
        $requestedMode = strtolower(trim((string)Request::get('mode', 'year')));
        $reportMode = in_array($requestedMode, ['year', 'range'], true) ? $requestedMode : 'year';
        $fromDate = trim((string)Request::get('from_date', sprintf('%04d-01-01', $year)));
        $toDate = trim((string)Request::get('to_date', sprintf('%04d-03-31', $year)));
        $filterError = null;
        $availableYears = [];

        if ($reportMode === 'range') {
            $from = DateTimeImmutable::createFromFormat('!Y-m-d', $fromDate);
            $fromErrors = DateTimeImmutable::getLastErrors();
            $fromOk = $from instanceof DateTimeImmutable && ($fromErrors === false || ((int)$fromErrors['warning_count'] === 0 && (int)$fromErrors['error_count'] === 0));
            $to = DateTimeImmutable::createFromFormat('!Y-m-d', $toDate);
            $toErrors = DateTimeImmutable::getLastErrors();
            $toOk = $to instanceof DateTimeImmutable && ($toErrors === false || ((int)$toErrors['warning_count'] === 0 && (int)$toErrors['error_count'] === 0));

            if (!$fromOk || !$toOk || $from > $to || $from->format('Y') !== $to->format('Y')) {
                $filterError = \t('diagnostics.invalid_range');
                $reportMode = 'year';
                $fromDate = sprintf('%04d-01-01', $year);
                $toDate = sprintf('%04d-03-31', $year);
            } else {
                $year = (int)$from->format('Y');
                if (!$compareYearWasRequested) {
                    $compareYear = $year - 1;
                }
            }
        }

        if ($reportMode === 'year') {
            try {
                $repo = new DiagnosticsRepository($hospitalCode);
                $availableYears = $repo->availableYears(20);
                if (!$yearWasRequested && !empty($availableYears)) {
                    $year = (int)$availableYears[0];
                }
                if (!$compareYearWasRequested && !empty($availableYears)) {
                    $compareYear = $this->defaultCompareYear($availableYears, $year);
                }
                $fromDate = sprintf('%04d-01-01', $year);
                $toDate = sprintf('%04d-03-31', $year);
            } catch (Throwable $e) {
                $this->logDiagnosticsError('Diagnostics year auto-detect warning', $e);
            }
        }

        $diagnosticsData = null;
        $dataError = null;
        try {
            $diagnosticsData = $this->buildData(
                $hospitalCode,
                $year,
                $compareYear,
                $reportMode,
                $fromDate,
                $toDate,
                $availableYears
            );
        } catch (Throwable $e) {
            $dataError = $e->getMessage();
            $this->logDiagnosticsError('Lab & Radiology workspace error', $e);
        }

        View::render('lab_radiology/index', [
            'title' => \t('diagnostics.title'),
            'activePage' => 'lab',
            'hospital' => $hospital,
            'hospitalCode' => $hospitalCode,
            'year' => $year,
            'compareYear' => $compareYear,
            'reportMode' => $reportMode,
            'fromDate' => $fromDate,
            'toDate' => $toDate,
            'filterError' => $filterError,
            'diagnosticsData' => $diagnosticsData,
            'dataError' => $dataError,
        ]);
    }

    /**
     * @param array<int, int> $years
     */
    private function defaultCompareYear(array $years, int $year): int
    {
        foreach ($years as $candidate) {
            $candidate = (int)$candidate;
            if ($candidate > 0 && $candidate < $year) {
                return $candidate;
            }
        }
        return $year - 1;
    }

    /**
     * @param array<int, int> $availableYears
     * @return array<string, mixed>
     */
    private function buildData(
        string $hospitalCode,
        int $year,
        int $compareYear,
        string $reportMode,
        string $fromDate,
        string $toDate,
        array $availableYears
    ): array {
        $repo = new DiagnosticsRepository($hospitalCode);
        $chartService = new ChartDataService();
        $rangeFrom = $reportMode === 'range' ? $fromDate : null;
        $rangeTo = $reportMode === 'range' ? $toDate : null;

        $labRows = $repo->monthlyLabTests($year, $compareYear, $rangeFrom, $rangeTo);
        $radiologyRows = $repo->monthlyRadiologyTests($year, $compareYear, $rangeFrom, $rangeTo);

        $currentPeriodLabel = $reportMode === 'range' ? ($fromDate . ' - ' . $toDate) : (string)$year;
        $comparePeriodLabel = $reportMode === 'range'
            ? (DiagnosticsRepository::shiftDateToYear($fromDate, $compareYear) . ' - ' . DiagnosticsRepository::shiftDateToYear($toDate, $compareYear))
            : (string)$compareYear;

        $labCurrent = $chartService->total($labRows, 'current_value');
        $labCompare = $chartService->total($labRows, 'compare_value');
        $radiologyCurrent = $chartService->total($radiologyRows, 'current_value');
        $radiologyCompare = $chartService->total($radiologyRows, 'compare_value');
        $combinedCurrent = $labCurrent + $radiologyCurrent;
        $combinedCompare = $labCompare + $radiologyCompare;
        $radiologyShareCurrent = $combinedCurrent > 0 ? ($radiologyCurrent / $combinedCurrent) * 100 : null;
        $radiologyShareCompare = $combinedCompare > 0 ? ($radiologyCompare / $combinedCompare) * 100 : null;

        $cards = [
            'lab' => $this->countCard(\t('diagnostics.card_lab'), $labCurrent, $labCompare, $chartService),
            'radiology' => $this->countCard(\t('diagnostics.card_radiology'), $radiologyCurrent, $radiologyCompare, $chartService),
            'combined' => $this->countCard(\t('diagnostics.card_combined'), $combinedCurrent, $combinedCompare, $chartService),
            'radiology_share' => [
                'title' => \t('diagnostics.card_radiology_share'),
                'current_total' => $radiologyShareCurrent,
                'compare_total' => $radiologyShareCompare,
                'percent' => ($radiologyShareCurrent !== null && $radiologyShareCompare !== null)
                    ? ($radiologyShareCurrent - $radiologyShareCompare)
                    : null,
                'mode' => 'percent',
            ],
        ];

        $movements = [];
        foreach (['lab', 'radiology'] as $key) {
            $card = $cards[$key];
            if (($card['percent'] ?? null) !== null) {
                $movements[] = [
                    'key' => $key,
                    'title' => (string)$card['title'],
                    'percent' => (float)$card['percent'],
                ];
            }
        }
        usort($movements, static function (array $a, array $b): int {
            return abs($b['percent']) <=> abs($a['percent']);
        });

        return [
            'mode' => $reportMode,
            'current_period_label' => $currentPeriodLabel,
            'compare_period_label' => $comparePeriodLabel,
            'availableYears' => $availableYears,
            'cards' => $cards,
            'summary' => [
                'lab_current' => $labCurrent,
                'lab_compare' => $labCompare,
                'radiology_current' => $radiologyCurrent,
                'radiology_compare' => $radiologyCompare,
                'combined_current' => $combinedCurrent,
                'combined_compare' => $combinedCompare,
                'radiology_share_current' => $radiologyShareCurrent,
                'radiology_share_compare' => $radiologyShareCompare,
                'primary_movement' => $movements[0] ?? null,
            ],
            'charts' => [
                'lab' => [
                    'title' => \t('diagnostics.chart_lab'),
                    'rows' => $labRows,
                    'payload' => $chartService->comparePayload($labRows, \t('diagnostics.chart_lab'), $currentPeriodLabel, $comparePeriodLabel),
                ],
                'radiology' => [
                    'title' => \t('diagnostics.chart_radiology'),
                    'rows' => $radiologyRows,
                    'payload' => $chartService->comparePayload($radiologyRows, \t('diagnostics.chart_radiology'), $currentPeriodLabel, $comparePeriodLabel),
                ],
            ],
        ];
    }

    /** @return array<string, mixed> */
    private function countCard(string $title, float $current, float $compare, ChartDataService $chartService): array
    {
        return [
            'title' => $title,
            'current_total' => $current,
            'compare_total' => $compare,
            'percent' => $chartService->percentChange($current, $compare),
            'mode' => 'count',
        ];
    }

    private function logDiagnosticsError(string $label, Throwable $e): void
    {
        $line = '[' . date('Y-m-d H:i:s') . '] ' . $label . ': ' . $e->getMessage() . PHP_EOL;
        $path = \base_path('storage/logs/app.log');
        @file_put_contents($path, $line, FILE_APPEND);
    }
}
