<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\AccessControl;
use App\Core\Auth;
use App\Core\Request;
use App\Core\View;
use App\Repositories\SpecialtyFinanceRepository;
use App\Services\ChartDataService;
use App\Services\FairYearComparisonPolicy;
use Throwable;

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

        if (!AccessControl::allows('specialty_finance.view')) {
            http_response_code(403);
            View::render('errors/403', [
                'title' => current_lang() === 'ar' ? 'غير مصرح بالدخول' : 'Access not available',
                'activePage' => '',
            ]);
            return;
        }

        $hospitalCode = Auth::hospital();
        $hospital = \config('hospitals.' . $hospitalCode, []);
        $currentYear = (int)date('Y');
        $yearRequested = Request::get('year') !== null;
        $compareRequested = Request::get('compare_year') !== null;
        $year = \safe_int(Request::get('year'), $currentYear);
        $compareYear = \safe_int(Request::get('compare_year'), $year - 1);

        $repo = new SpecialtyFinanceRepository($hospitalCode);
        $years = [];
        try {
            $years = $repo->availableYears(20);
            if (!in_array($currentYear, $years, true)) {
                array_unshift($years, $currentYear);
            }
            $years = array_values(array_unique(array_map('intval', $years)));
            rsort($years, SORT_NUMERIC);

            if (!$yearRequested) {
                $year = $currentYear;
            }
            if (!$compareRequested) {
                $compareYear = $this->defaultCompareYear($years, $year);
            }
        } catch (Throwable $e) {
            $this->logError('Specialty Finance filter discovery warning', $e);
        }

        $comparisonContract = (new FairYearComparisonPolicy())->resolve($year, $compareYear);

        $financeData = null;
        $dataError = null;
        try {
            $financeData = $this->buildData($repo, $year, $compareYear, $years, $comparisonContract);
        } catch (Throwable $e) {
            $dataError = $e->getMessage();
            $this->logError('Specialty Finance workspace error', $e);
        }

        View::render('specialty_finance/index', [
            'title' => current_lang() === 'ar' ? 'المالية حسب التخصص' : 'Specialty Finance',
            'activePage' => 'specialty-finance',
            'hospital' => $hospital,
            'hospitalCode' => $hospitalCode,
            'year' => $year,
            'compareYear' => $compareYear,
            'financeData' => $financeData,
            'comparisonContract' => $comparisonContract,
            '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> $years @return array<string,mixed> */
    private function buildData(
        SpecialtyFinanceRepository $repo,
        int $year,
        int $compareYear,
        array $years,
        array $comparisonContract
    ): array {
        $chartService = new ChartDataService();
        $startMonth = (int)($comparisonContract['start_month'] ?? 1);
        $endMonth = (int)($comparisonContract['end_month'] ?? 12);
        $hasComparableMonths = $endMonth >= $startMonth && $endMonth > 0;
        $currentCoverage = $repo->yearCoverage($year);
        $compareCoverage = $repo->yearCoverage($compareYear);
        $currentMonthCoverage = $repo->monthCoverage($year);
        $compareMonthCoverage = $repo->monthCoverage($compareYear);

        $summary = $hasComparableMonths
            ? $repo->summary($year, $compareYear, $startMonth, $endMonth)
            : [
                'year' => $year,
                'compare_year' => $compareYear,
                'current_moh' => 0,
                'compare_moh' => 0,
                'current_drug' => 0,
                'compare_drug' => 0,
                'current_net' => 0,
                'compare_net' => 0,
            ];
        $moh = $hasComparableMonths ? $repo->specialtyRanking($year, 'MOH', 10, $startMonth, $endMonth) : [];
        $drug = $hasComparableMonths ? $repo->specialtyRanking($year, 'DRUG_REV', 10, $startMonth, $endMonth) : [];
        $net = $hasComparableMonths ? $repo->specialtyNetRevenueRanking($year, $startMonth, $endMonth) : [];
        $totalRevenueBySpecialty = $hasComparableMonths ? $repo->specialtyTotalRevenueRanking($year, $startMonth, $endMonth) : [];
        $matrix = $this->mergeExactLegacySpecialtyMetricsIntoMatrix(
            $hasComparableMonths ? $repo->specialtyMatrix($year, 12, $startMonth, $endMonth) : [],
            $totalRevenueBySpecialty,
            $net
        );

        $activityPeriodRows = $hasComparableMonths
            ? $repo->specialtyActivityComparison(
                $year,
                $compareYear,
                $startMonth,
                $endMonth
            )
            : [];
        $activityMonthlyRows = $hasComparableMonths
            ? $repo->specialtyActivityMonthlyComparison(
                $year,
                $compareYear,
                $startMonth,
                $endMonth
            )
            : [];
        $activity = $this->buildActivityWorkspace(
            $activityPeriodRows,
            $activityMonthlyRows,
            $year,
            $compareYear,
            $startMonth,
            $endMonth,
            $chartService
        );

        $cards = [
            'moh' => $this->card('MOH revenue', (float)$summary['current_moh'], (float)$summary['compare_moh'], $chartService),
            'drug' => $this->card('Drug revenue', (float)$summary['current_drug'], (float)$summary['compare_drug'], $chartService),
            'net' => $this->card('Net after drug / MOH', (float)$summary['current_net'], (float)$summary['compare_net'], $chartService),
        ];

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

        return [
            'summary' => $summary,
            'cards' => $cards,
            'availableYears' => $years,
            'coverage' => [
                'current' => $currentCoverage,
                'compare' => $compareCoverage,
                'current_months' => $currentMonthCoverage,
                'compare_months' => $compareMonthCoverage,
                'current_year_available' => in_array((int)date('Y'), $years, true),
            ],
            'comparisonContract' => $comparisonContract,
            'periodLabels' => [
                'current' => $this->periodLabel($year, $endMonth),
                'compare' => $this->periodLabel($compareYear, $endMonth),
            ],
            'rankings' => [
                'moh' => $moh,
                'drug' => $drug,
                'net' => $net,
                'total_revenue' => $totalRevenueBySpecialty,
            ],
            'matrix' => $matrix,
            'activity' => $activity,
            'primaryMovement' => $movements[0] ?? null,
            'topNetSpecialty' => $net[0] ?? null,
            'topTotalRevenueSpecialty' => $totalRevenueBySpecialty[0] ?? null,
            'charts' => [
                'moh' => ['title' => 'MOH contribution by specialty', 'payload' => $this->payload('MOH contribution by specialty', $moh, 'MOH revenue')],
                'drug' => ['title' => 'Drug revenue by specialty', 'payload' => $this->payload('Drug revenue by specialty', $drug, 'Drug revenue')],
                'net' => ['title' => 'Net revenue by specialty', 'payload' => $this->payload('Net revenue by specialty', $net, 'Net revenue')],
            ],
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $periodRows
     * @param array<int, array<string, mixed>> $monthlyRows
     * @return array<string, mixed>
     */
    private function buildActivityWorkspace(
        array $periodRows,
        array $monthlyRows,
        int $year,
        int $compareYear,
        int $startMonth,
        int $endMonth,
        ChartDataService $chartService
    ): array {
        $rows = [];
        $totals = [
            'current_outpatient_visits' => 0.0,
            'compare_outpatient_visits' => 0.0,
            'current_admissions' => 0.0,
            'compare_admissions' => 0.0,
        ];

        foreach ($periodRows as $row) {
            $currentOutpatient = (float)($row['current_outpatient_visits'] ?? 0);
            $compareOutpatient = (float)($row['compare_outpatient_visits'] ?? 0);
            $currentAdmissions = (float)($row['current_admissions'] ?? 0);
            $compareAdmissions = (float)($row['compare_admissions'] ?? 0);

            $normalized = [
                'id' => trim((string)($row['id'] ?? '')),
                'label' => trim((string)($row['label'] ?? 'Unknown specialty')),
                'current_outpatient_visits' => $currentOutpatient,
                'compare_outpatient_visits' => $compareOutpatient,
                'outpatient_difference' => $currentOutpatient - $compareOutpatient,
                'outpatient_percent' => $chartService->percentChange(
                    $currentOutpatient,
                    $compareOutpatient
                ),
                'outpatient_state' => $this->activityState(
                    $currentOutpatient,
                    $compareOutpatient
                ),
                'current_admissions' => $currentAdmissions,
                'compare_admissions' => $compareAdmissions,
                'admissions_difference' => $currentAdmissions - $compareAdmissions,
                'admissions_percent' => $chartService->percentChange(
                    $currentAdmissions,
                    $compareAdmissions
                ),
                'admissions_state' => $this->activityState(
                    $currentAdmissions,
                    $compareAdmissions
                ),
            ];

            $rows[] = $normalized;

            $totals['current_outpatient_visits'] += $currentOutpatient;
            $totals['compare_outpatient_visits'] += $compareOutpatient;
            $totals['current_admissions'] += $currentAdmissions;
            $totals['compare_admissions'] += $compareAdmissions;
        }

        $tableRows = $rows;
        usort($tableRows, static function (array $a, array $b): int {
            $labelCompare = strcasecmp(
                (string)($a['label'] ?? ''),
                (string)($b['label'] ?? '')
            );
            if ($labelCompare !== 0) {
                return $labelCompare;
            }
            return strcmp((string)($a['id'] ?? ''), (string)($b['id'] ?? ''));
        });

        $outpatientRanking = $rows;
        usort($outpatientRanking, static function (array $a, array $b): int {
            $valueCompare = ((float)($b['current_outpatient_visits'] ?? 0))
                <=> ((float)($a['current_outpatient_visits'] ?? 0));
            if ($valueCompare !== 0) {
                return $valueCompare;
            }
            return strcasecmp(
                (string)($a['label'] ?? ''),
                (string)($b['label'] ?? '')
            );
        });

        $admissionsRanking = $rows;
        usort($admissionsRanking, static function (array $a, array $b): int {
            $valueCompare = ((float)($b['current_admissions'] ?? 0))
                <=> ((float)($a['current_admissions'] ?? 0));
            if ($valueCompare !== 0) {
                return $valueCompare;
            }
            return strcasecmp(
                (string)($a['label'] ?? ''),
                (string)($b['label'] ?? '')
            );
        });

        $outpatientDeclines = array_values(array_filter(
            $rows,
            static function (array $row): bool {
                return (float)($row['outpatient_difference'] ?? 0) < 0;
            }
        ));
        usort($outpatientDeclines, static function (array $a, array $b): int {
            return ((float)($a['outpatient_difference'] ?? 0))
                <=> ((float)($b['outpatient_difference'] ?? 0));
        });

        $admissionsDeclines = array_values(array_filter(
            $rows,
            static function (array $row): bool {
                return (float)($row['admissions_difference'] ?? 0) < 0;
            }
        ));
        usort($admissionsDeclines, static function (array $a, array $b): int {
            return ((float)($a['admissions_difference'] ?? 0))
                <=> ((float)($b['admissions_difference'] ?? 0));
        });

        $monthlyBySpecialty = [];
        foreach ($monthlyRows as $row) {
            $specialtyId = trim((string)($row['id'] ?? ''));
            $monthId = (int)($row['month_id'] ?? 0);
            if ($specialtyId === '' || $monthId < $startMonth || $monthId > $endMonth) {
                continue;
            }

            if (!isset($monthlyBySpecialty[$specialtyId])) {
                $monthlyBySpecialty[$specialtyId] = [];
            }

            $monthlyBySpecialty[$specialtyId][$monthId] = [
                'current_outpatient_visits' => (float)(
                    $row['current_outpatient_visits'] ?? 0
                ),
                'compare_outpatient_visits' => (float)(
                    $row['compare_outpatient_visits'] ?? 0
                ),
                'current_admissions' => (float)($row['current_admissions'] ?? 0),
                'compare_admissions' => (float)($row['compare_admissions'] ?? 0),
            ];
        }

        $aggregateMonthly = [];
        for ($month = $startMonth; $month <= $endMonth; $month++) {
            $aggregateMonthly[$month] = [
                'current_outpatient_visits' => 0.0,
                'compare_outpatient_visits' => 0.0,
                'current_admissions' => 0.0,
                'compare_admissions' => 0.0,
            ];
        }

        foreach ($monthlyRows as $row) {
            $monthId = (int)($row['month_id'] ?? 0);
            if (!isset($aggregateMonthly[$monthId])) {
                continue;
            }
            $aggregateMonthly[$monthId]['current_outpatient_visits'] += (float)($row['current_outpatient_visits'] ?? 0);
            $aggregateMonthly[$monthId]['compare_outpatient_visits'] += (float)($row['compare_outpatient_visits'] ?? 0);
            $aggregateMonthly[$monthId]['current_admissions'] += (float)($row['current_admissions'] ?? 0);
            $aggregateMonthly[$monthId]['compare_admissions'] += (float)($row['compare_admissions'] ?? 0);
        }

        $aggregateLabels = [];
        $aggregateCurrentOutpatient = [];
        $aggregateCompareOutpatient = [];
        $aggregateCurrentAdmissions = [];
        $aggregateCompareAdmissions = [];

        foreach ($aggregateMonthly as $month => $monthRow) {
            $aggregateLabels[] = $this->monthShortLabel((int)$month);
            $aggregateCurrentOutpatient[] = (float)$monthRow['current_outpatient_visits'];
            $aggregateCompareOutpatient[] = (float)$monthRow['compare_outpatient_visits'];
            $aggregateCurrentAdmissions[] = (float)$monthRow['current_admissions'];
            $aggregateCompareAdmissions[] = (float)$monthRow['compare_admissions'];
        }

        $chartConfigs = [[
            'id' => '__all__',
            'label' => current_lang() === 'ar' ? 'جميع التخصصات' : 'All Specialties',
            'outpatient' => [
                'title' => 'Outpatient visits by month',
                'labels' => $aggregateLabels,
                'series' => [
                    ['label' => (string)$year, 'values' => $aggregateCurrentOutpatient],
                    ['label' => (string)$compareYear, 'values' => $aggregateCompareOutpatient],
                ],
                'showHeader' => false,
            ],
            'admissions' => [
                'title' => 'Admissions by month',
                'labels' => $aggregateLabels,
                'series' => [
                    ['label' => (string)$year, 'values' => $aggregateCurrentAdmissions],
                    ['label' => (string)$compareYear, 'values' => $aggregateCompareAdmissions],
                ],
                'showHeader' => false,
            ],
        ]];
        foreach ($tableRows as $row) {
            $specialtyId = (string)$row['id'];
            $labels = [];
            $currentOutpatient = [];
            $compareOutpatient = [];
            $currentAdmissions = [];
            $compareAdmissions = [];

            for ($month = $startMonth; $month <= $endMonth; $month++) {
                $monthRow = (array)($monthlyBySpecialty[$specialtyId][$month] ?? []);
                $labels[] = $this->monthShortLabel($month);
                $currentOutpatient[] = (float)(
                    $monthRow['current_outpatient_visits'] ?? 0
                );
                $compareOutpatient[] = (float)(
                    $monthRow['compare_outpatient_visits'] ?? 0
                );
                $currentAdmissions[] = (float)(
                    $monthRow['current_admissions'] ?? 0
                );
                $compareAdmissions[] = (float)(
                    $monthRow['compare_admissions'] ?? 0
                );
            }

            $chartConfigs[] = [
                'id' => $specialtyId,
                'label' => (string)$row['label'],
                'outpatient' => [
                    'title' => 'Outpatient visits by month',
                    'labels' => $labels,
                    'series' => [
                        [
                            'label' => (string)$year,
                            'values' => $currentOutpatient,
                        ],
                        [
                            'label' => (string)$compareYear,
                            'values' => $compareOutpatient,
                        ],
                    ],
                    'showHeader' => false,
                ],
                'admissions' => [
                    'title' => 'Admissions by month',
                    'labels' => $labels,
                    'series' => [
                        [
                            'label' => (string)$year,
                            'values' => $currentAdmissions,
                        ],
                        [
                            'label' => (string)$compareYear,
                            'values' => $compareAdmissions,
                        ],
                    ],
                    'showHeader' => false,
                ],
            ];
        }

        return [
            'ready' => $rows !== [],
            'source_verified' => true,
            'rows' => $tableRows,
            'totals' => $totals,
            'cards' => [
                'outpatient' => $this->card(
                    'Outpatient visits',
                    $totals['current_outpatient_visits'],
                    $totals['compare_outpatient_visits'],
                    $chartService
                ),
                'admissions' => $this->card(
                    'Admissions',
                    $totals['current_admissions'],
                    $totals['compare_admissions'],
                    $chartService
                ),
            ],
            'signals' => [
                'top_outpatient' => $outpatientRanking[0] ?? null,
                'top_admissions' => $admissionsRanking[0] ?? null,
                'largest_outpatient_decline' => $outpatientDeclines[0] ?? null,
                'largest_admissions_decline' => $admissionsDeclines[0] ?? null,
            ],
            'chart_configs' => $chartConfigs,
            'default_specialty_id' => '__all__',
            'default_metric' => 'outpatient',
            'period' => [
                'year' => $year,
                'compare_year' => $compareYear,
                'start_month' => $startMonth,
                'end_month' => $endMonth,
            ],
            'revenue_activity_ratio_available' => false,
            'oracle_writes' => 'NONE',
        ];
    }

    private function activityState(float $current, float $compare): string
    {
        if ($current > 0 && $compare <= 0) {
            return 'new_activity';
        }
        if ($current <= 0 && $compare > 0) {
            return 'no_current_activity';
        }
        if ($current > $compare) {
            return 'increased';
        }
        if ($current < $compare) {
            return 'decreased';
        }
        return 'stable';
    }

    private function monthShortLabel(int $month): 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 => 'ديسمبر',
        ];
        $labels = current_lang() === 'ar' ? $ar : $en;

        return (string)($labels[$month] ?? $month);
    }

    /**
     * @param array<int, array<string, mixed>> $matrix
     * @param array<int, array<string, mixed>> $totalRevenueRows
     * @param array<int, array<string, mixed>> $netRevenueRows
     * @return array<int, array<string, mixed>>
     */
    private function mergeExactLegacySpecialtyMetricsIntoMatrix(
        array $matrix,
        array $totalRevenueRows,
        array $netRevenueRows
    ): array {
        $totalRevenueMap = [];
        $netRevenueMap = [];

        foreach ($totalRevenueRows as $row) {
            $id = trim((string)($row['id'] ?? ''));

            if ($id !== '') {
                $totalRevenueMap[$id] = (float)($row['value'] ?? 0);
            }
        }

        foreach ($netRevenueRows as $row) {
            $id = trim((string)($row['id'] ?? ''));

            if ($id !== '') {
                $netRevenueMap[$id] = (float)($row['value'] ?? 0);
            }
        }

        foreach ($matrix as &$row) {
            $id = trim((string)($row['id'] ?? ''));

            $row['total_revenue_value'] = array_key_exists($id, $totalRevenueMap)
                ? $totalRevenueMap[$id]
                : null;

            $row['net_value'] = array_key_exists($id, $netRevenueMap)
                ? $netRevenueMap[$id]
                : null;
        }
        unset($row);

        return $matrix;
    }

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

    /** @param array<int,array<string,mixed>> $rows @return array<string,mixed> */
    private function payload(string $title, array $rows, string $seriesLabel): array
    {
        return [
            'title' => $title,
            'labels' => array_map(static function (array $row): string { return (string)($row['label'] ?? ''); }, $rows),
            'series' => [[
                'label' => $seriesLabel,
                'values' => array_map(static function (array $row): float { return (float)($row['value'] ?? 0); }, $rows),
            ]],
        ];
    }

    private function periodLabel(int $year, int $endMonth): string
    {
        if ($endMonth <= 0) {
            return current_lang() === 'ar'
                ? 'لا توجد أشهر مكتملة في ' . $year
                : 'No completed months in ' . $year;
        }

        $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[$endMonth] ?? (string)$endMonth) . ' ' . $year;
    }

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