<?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 App\Services\FairYearComparisonPolicy;
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', date('Y-m-d')));
        $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 = date('Y-m-d');
            } 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 = date('Y-m-d');
            } catch (Throwable $e) {
                $this->logDiagnosticsError('Diagnostics year auto-detect warning', $e);
            }
        }

        foreach ([$currentYear, $year, $compareYear] as $requiredYear) {
            if ($requiredYear > 0 && !in_array($requiredYear, $availableYears, true)) {
                $availableYears[] = $requiredYear;
            }
        }
        $availableYears = array_values(array_unique(array_map('intval', $availableYears)));
        rsort($availableYears, SORT_NUMERIC);

        $comparisonContract = $reportMode === 'year'
            ? (new FairYearComparisonPolicy())->resolve($year, $compareYear)
            : [];

        $diagnosticsData = null;
        $dataError = null;
        try {
            $diagnosticsData = $this->buildData(
                $hospitalCode,
                $year,
                $compareYear,
                $reportMode,
                $fromDate,
                $toDate,
                $availableYears,
                $comparisonContract
            );
        } 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,
            'comparisonContract' => $comparisonContract,
            '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 $comparisonContract = []
    ): array {
        $repo = new DiagnosticsRepository($hospitalCode);
        $chartService = new ChartDataService();

        $isFairCurrentYearComparison = $reportMode === 'year'
            && !empty($comparisonContract['fair_current_year_comparison']);
        $lastCompleteMonth = $isFairCurrentYearComparison
            ? (int)($comparisonContract['last_complete_month'] ?? 0)
            : 12;
        $hasComparableMonths = !$isFairCurrentYearComparison || $lastCompleteMonth > 0;

        if ($reportMode === 'range') {
            $rangeFrom = $fromDate;
            $rangeTo = $toDate;
            $visibleStartMonth = (int)(new DateTimeImmutable($fromDate))->format('n');
            $visibleEndMonth = (int)(new DateTimeImmutable($toDate))->format('n');
            $currentPeriodLabel = $fromDate . ' - ' . $toDate;
            $comparePeriodLabel = DiagnosticsRepository::shiftDateToYear($fromDate, $compareYear)
                . ' - '
                . DiagnosticsRepository::shiftDateToYear($toDate, $compareYear);
        } elseif ($isFairCurrentYearComparison) {
            $rangeFrom = isset($comparisonContract['from_date'])
                ? (string)$comparisonContract['from_date']
                : sprintf('%04d-01-01', $year);
            $rangeTo = isset($comparisonContract['to_date'])
                ? (string)$comparisonContract['to_date']
                : sprintf('%04d-%02d-01', $year, max(1, $lastCompleteMonth));
            $visibleStartMonth = 1;
            $visibleEndMonth = $lastCompleteMonth;
            $currentPeriodLabel = $lastCompleteMonth > 0
                ? $this->monthRangeLabel($year, $lastCompleteMonth)
                : (\current_lang() === 'ar' ? 'لا توجد أشهر مكتملة في ' . $year : 'No completed months in ' . $year);
            $comparePeriodLabel = $lastCompleteMonth > 0
                ? $this->monthRangeLabel($compareYear, $lastCompleteMonth)
                : (\current_lang() === 'ar' ? 'لا توجد أشهر قابلة للمقارنة' : 'No comparable completed months');
        } else {
            $rangeFrom = sprintf('%04d-01-01', $year);
            $rangeTo = sprintf('%04d-12-31', $year);
            $visibleStartMonth = 1;
            $visibleEndMonth = 12;
            $currentPeriodLabel = $this->monthRangeLabel($year, 12);
            $comparePeriodLabel = $this->monthRangeLabel($compareYear, 12);
        }

        $labRows = $hasComparableMonths
            ? $repo->monthlyLabTests($year, $compareYear, $rangeFrom, $rangeTo)
            : [];
        $radiologyRows = $hasComparableMonths
            ? $repo->monthlyRadiologyTests($year, $compareYear, $rangeFrom, $rangeTo)
            : [];

        $labRows = $this->filterMonthlyRows($labRows, $visibleStartMonth, $visibleEndMonth);
        $radiologyRows = $this->filterMonthlyRows($radiologyRows, $visibleStartMonth, $visibleEndMonth);

        $combinedRows = [];
        $labByMonth = [];
        foreach ($labRows as $row) {
            $labByMonth[(int)($row['month_id'] ?? 0)] = $row;
        }
        $radiologyByMonth = [];
        foreach ($radiologyRows as $row) {
            $radiologyByMonth[(int)($row['month_id'] ?? 0)] = $row;
        }

        for ($month = $visibleStartMonth; $month <= $visibleEndMonth; $month++) {
            $labRow = $labByMonth[$month] ?? [];
            $radiologyRow = $radiologyByMonth[$month] ?? [];
            $combinedRows[] = [
                'month_id' => $month,
                'month_name' => (string)(
                    $labRow['month_name']
                    ?? $radiologyRow['month_name']
                    ?? $this->monthName($month)
                ),
                'current_value' => (float)($labRow['current_value'] ?? 0)
                    + (float)($radiologyRow['current_value'] ?? 0),
                'compare_value' => (float)($labRow['compare_value'] ?? 0)
                    + (float)($radiologyRow['compare_value'] ?? 0),
            ];
        }

        $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;
        $labShareCurrent = $combinedCurrent > 0 ? ($labCurrent / $combinedCurrent) * 100 : null;
        $labShareCompare = $combinedCompare > 0 ? ($labCompare / $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']);
        });

        $chartPayload = static function (array $payload): array {
            $payload['showHeader'] = false;
            $payload['showLegend'] = true;
            $payload['showDataLabels'] = true;
            $payload['stableHover'] = true;
            return $payload;
        };

        return [
            'mode' => $reportMode,
            'comparison_contract' => $comparisonContract,
            'current_period_label' => $currentPeriodLabel,
            'compare_period_label' => $comparePeriodLabel,
            'visible_start_month' => $visibleStartMonth,
            'visible_end_month' => $visibleEndMonth,
            'availableYears' => $availableYears,
            'comparison_ready' => $combinedCurrent > 0 && $combinedCompare > 0,
            '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,
                'lab_share_current' => $labShareCurrent,
                'lab_share_compare' => $labShareCompare,
                'primary_movement' => $movements[0] ?? null,
            ],
            'monthly' => $combinedRows,
            'charts' => [
                'lab' => [
                    'title' => \t('diagnostics.chart_lab'),
                    'rows' => $labRows,
                    'payload' => $chartPayload($chartService->comparePayload(
                        $labRows,
                        \t('diagnostics.chart_lab'),
                        $currentPeriodLabel,
                        $comparePeriodLabel
                    )),
                ],
                'radiology' => [
                    'title' => \t('diagnostics.chart_radiology'),
                    'rows' => $radiologyRows,
                    'payload' => $chartPayload($chartService->comparePayload(
                        $radiologyRows,
                        \t('diagnostics.chart_radiology'),
                        $currentPeriodLabel,
                        $comparePeriodLabel
                    )),
                ],
                'combined' => [
                    'title' => \current_lang() === 'ar' ? 'إجمالي النشاط التشخيصي' : 'Total diagnostic activity',
                    'rows' => $combinedRows,
                    'payload' => $chartPayload($chartService->comparePayload(
                        $combinedRows,
                        \current_lang() === 'ar' ? 'إجمالي النشاط التشخيصي' : 'Total diagnostic activity',
                        $currentPeriodLabel,
                        $comparePeriodLabel
                    )),
                ],
            ],
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $rows
     * @return array<int, array<string, mixed>>
     */
    private function filterMonthlyRows(array $rows, int $startMonth, int $endMonth): array
    {
        if ($endMonth < $startMonth || $endMonth < 1) {
            return [];
        }

        return array_values(array_filter($rows, static function (array $row) use ($startMonth, $endMonth): bool {
            $month = (int)($row['month_id'] ?? 0);
            return $month >= $startMonth && $month <= $endMonth;
        }));
    }

    private function monthRangeLabel(int $year, int $lastMonth): string
    {
        $en = [1=>'Jan',2=>'Feb',3=>'Mar',4=>'Apr',5=>'May',6=>'Jun',7=>'Jul',8=>'Aug',9=>'Sep',10=>'Oct',11=>'Nov',12=>'Dec'];
        $ar = [1=>'يناير',2=>'فبراير',3=>'مارس',4=>'أبريل',5=>'مايو',6=>'يونيو',7=>'يوليو',8=>'أغسطس',9=>'سبتمبر',10=>'أكتوبر',11=>'نوفمبر',12=>'ديسمبر'];
        $months = \current_lang() === 'ar' ? $ar : $en;

        return ($months[1] ?? 'Jan') . '–' . ($months[$lastMonth] ?? (string)$lastMonth) . ' ' . $year;
    }

    private function monthName(int $month): string
    {
        $en = [1=>'January',2=>'February',3=>'March',4=>'April',5=>'May',6=>'June',7=>'July',8=>'August',9=>'September',10=>'October',11=>'November',12=>'December'];
        $ar = [1=>'يناير',2=>'فبراير',3=>'مارس',4=>'أبريل',5=>'مايو',6=>'يونيو',7=>'يوليو',8=>'أغسطس',9=>'سبتمبر',10=>'أكتوبر',11=>'نوفمبر',12=>'ديسمبر'];
        $months = \current_lang() === 'ar' ? $ar : $en;
        return $months[$month] ?? (string)$month;
    }

    /** @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);
    }
}
