<?php

declare(strict_types=1);

namespace App\Services;

final class InpatientDailyRevenueService
{
    private const NATIONAL_INSURANCE_ID = 365;
    private const TCS_ID = 170;
    private const CASH_ID = 1;

    /**
     * @param array<int, array<string, mixed>> $rows
     * @return array{patients:array<int,array<string,mixed>>,summary:array<string,mixed>,units:array<int,array<string,mixed>>,companies:array<int,array<string,mixed>>}
     */
    public function build(array $rows): array
    {
        $patients = [];
        $unitMap = [];
        $companyMap = [];
        $totalRevenue = 0.0;
        $reviewCount = 0;
        $automaticCount = 0;

        foreach ($rows as $row) {
            $patient = $this->calculatePatient($row);
            $patients[] = $patient;

            $daily = (float)$patient['daily_revenue'];
            $totalRevenue += $daily;
            if ($patient['needs_review']) {
                $reviewCount++;
            } else {
                $automaticCount++;
            }

            $unitKey = trim((string)($patient['unit_name'] ?? '')) ?: 'Unassigned';
            if (!isset($unitMap[$unitKey])) {
                $unitMap[$unitKey] = [
                    'unit_name' => $unitKey,
                    'patients' => 0,
                    'beds' => (int)($patient['bed_counts'] ?? 0),
                    'daily_revenue' => 0.0,
                ];
            }
            $unitMap[$unitKey]['patients']++;
            $unitMap[$unitKey]['daily_revenue'] += $daily;
            $unitMap[$unitKey]['beds'] = max((int)$unitMap[$unitKey]['beds'], (int)($patient['bed_counts'] ?? 0));

            $companyId = (int)$patient['financlassid'];
            $companyKey = $companyId . '|' . (string)$patient['financial_class'];
            if (!isset($companyMap[$companyKey])) {
                $companyMap[$companyKey] = [
                    'financlassid' => $companyId,
                    'financial_class' => (string)$patient['financial_class'],
                    'patients' => 0,
                    'daily_revenue' => 0.0,
                ];
            }
            $companyMap[$companyKey]['patients']++;
            $companyMap[$companyKey]['daily_revenue'] += $daily;
        }

        $units = array_values($unitMap);
        usort($units, static fn(array $a, array $b): int => $b['daily_revenue'] <=> $a['daily_revenue']);

        $companies = array_values($companyMap);
        usort($companies, static fn(array $a, array $b): int => $b['daily_revenue'] <=> $a['daily_revenue']);

        $count = count($patients);
        return [
            'patients' => $patients,
            'summary' => [
                'patient_count' => $count,
                'daily_revenue' => round($totalRevenue, 2),
                'average_revenue' => $count > 0 ? round($totalRevenue / $count, 2) : 0.0,
                'automatic_count' => $automaticCount,
                'automatic_pct' => $count > 0 ? round(($automaticCount / $count) * 100, 1) : 0.0,
                'review_count' => $reviewCount,
                'generated_at' => date('Y-m-d H:i:s'),
            ],
            'units' => $units,
            'companies' => $companies,
        ];
    }

    /** @param array<string,mixed> $row @return array<string,mixed> */
    private function calculatePatient(array $row): array
    {
        $companyId = (int)($row['financlassid'] ?? 0);
        $los = max(1, (int)round((float)($row['los'] ?? 1)));
        $totalLos = max(1, (int)round((float)($row['total_los'] ?? $los)));
        $patientShare = (float)($row['patient_share_total'] ?? 0);
        $companyShare = (float)($row['company_share_total'] ?? 0);
        $methodCode = 'insurance_share_per_los';
        $methodLabel = 'Insurance company share ÷ LOS';
        $dailyRevenue = $companyShare / $los;

        if ($companyId === self::NATIONAL_INSURANCE_ID) {
            $dailyRevenue = $totalLos > 30 ? 2373.00 : 4746.87;
            $methodCode = $totalLos > 30 ? 'nhic_over_30' : 'nhic_30_or_less';
            $methodLabel = $totalLos > 30
                ? 'National insurance fixed rate — over 30 days'
                : 'National insurance fixed rate — 30 days or less';
        } elseif ($companyId === self::TCS_ID) {
            if ($los <= 3) {
                $dailyRevenue = 2250.00;
                $methodCode = 'tcs_1_3';
                $methodLabel = 'TCS LOS tier — days 1–3';
            } elseif ($los <= 8) {
                $dailyRevenue = 1950.00;
                $methodCode = 'tcs_4_8';
                $methodLabel = 'TCS LOS tier — days 4–8';
            } else {
                $dailyRevenue = 1750.00;
                $methodCode = 'tcs_9_plus';
                $methodLabel = 'TCS LOS tier — day 9 and above';
            }
        } elseif ($companyId === self::CASH_ID) {
            $dailyRevenue = $patientShare / $los;
            $methodCode = 'cash_patient_share_per_los';
            $methodLabel = 'Cash patient share ÷ LOS';
        }

        $needsReview = false;
        $reviewReason = '';
        if (!is_finite($dailyRevenue) || $dailyRevenue < 0) {
            $needsReview = true;
            $reviewReason = 'Calculated daily revenue is negative or invalid.';
        } elseif (!in_array($companyId, [self::NATIONAL_INSURANCE_ID, self::TCS_ID], true)
            && abs($companyId === self::CASH_ID ? $patientShare : $companyShare) < 0.00001) {
            $needsReview = true;
            $reviewReason = 'No accumulated share was found for the active visit.';
        }

        return array_merge($row, [
            'los' => $los,
            'total_los' => $totalLos,
            'patient_share_total' => round($patientShare, 2),
            'company_share_total' => round($companyShare, 2),
            'daily_revenue' => round(max(0.0, $dailyRevenue), 2),
            'method_code' => $methodCode,
            'method_label' => $methodLabel,
            'needs_review' => $needsReview,
            'review_reason' => $reviewReason,
        ]);
    }
}
