<?php
declare(strict_types=1);

require dirname(__DIR__) . '/app/bootstrap.php';

use App\Repositories\VisitsRepository;

function profile_call(string $name, callable $callback): array
{
    $started = microtime(true);
    $memoryBefore = memory_get_usage(true);
    try {
        $result = $callback();
        $elapsed = round((microtime(true) - $started) * 1000, 2);
        return [
            'dataset' => $name,
            'status' => 'PASS',
            'elapsed_ms' => $elapsed,
            'rows' => is_array($result) ? count($result) : null,
            'memory_delta_kb' => round((memory_get_usage(true) - $memoryBefore) / 1024, 2),
            'error' => null,
        ];
    } catch (Throwable $e) {
        return [
            'dataset' => $name,
            'status' => 'FAIL',
            'elapsed_ms' => round((microtime(true) - $started) * 1000, 2),
            'rows' => null,
            'memory_delta_kb' => round((memory_get_usage(true) - $memoryBefore) / 1024, 2),
            'error' => $e->getMessage(),
        ];
    }
}

function run_profile(
    string $hospital,
    int $year,
    int $compareYear,
    string $mode,
    ?string $fromDate,
    ?string $toDate,
    int $iterations
): array {
    $runs = [];
    $overallStarted = microtime(true);

    for ($iteration = 1; $iteration <= $iterations; $iteration++) {
        // A new repository per iteration gives a realistic request-level cache boundary.
        $repo = new VisitsRepository($hospital);
        $rangeFrom = $mode === 'range' ? $fromDate : null;
        $rangeTo = $mode === 'range' ? $toDate : null;
        $items = [];

        $items[] = profile_call('monthly_opd_er_consolidated_bundle', function () use ($repo, $year, $compareYear, $rangeFrom, $rangeTo) {
            $bundle = $repo->monthlyVisitBundle($year, $compareYear, $rangeFrom, $rangeTo);
            return array_merge($bundle['opd'] ?? [], $bundle['er'] ?? []);
        });
        $items[] = profile_call('monthly_admissions', function () use ($repo, $year, $compareYear, $rangeFrom, $rangeTo) {
            return $repo->monthlyAdmissions($year, $compareYear, $rangeFrom, $rangeTo);
        });
        $items[] = profile_call('followup_mix', function () use ($repo, $year, $rangeFrom, $rangeTo) {
            return $repo->followupMix($year, $rangeFrom, $rangeTo);
        });
        $items[] = profile_call('admission_category_mix', function () use ($repo, $year, $rangeFrom, $rangeTo) {
            return $repo->admissionCategoryMix($year, $rangeFrom, $rangeTo);
        });
        $items[] = profile_call('outpatient_cash_credit_today', function () use ($repo) {
            return $repo->outpatientCashCreditToday();
        });
        $items[] = profile_call('daily_outpatient_trend_14d', function () use ($repo) {
            return $repo->dailyOutpatientTrend(14);
        });
        $items[] = profile_call('top_specialties_by_op_visits', function () use ($repo, $year) {
            return $repo->topSpecialtiesByOpVisits($year);
        });

        $sum = 0.0;
        $failed = 0;
        foreach ($items as $item) {
            $sum += (float)$item['elapsed_ms'];
            if ($item['status'] !== 'PASS') $failed++;
        }

        usort($items, function (array $a, array $b): int {
            return $a['elapsed_ms'] < $b['elapsed_ms'] ? 1 : -1;
        });

        $runs[] = [
            'iteration' => $iteration,
            'dataset_total_ms' => round($sum, 2),
            'failed_datasets' => $failed,
            'datasets_slowest_first' => $items,
        ];
    }

    $aggregate = [];
    foreach ($runs as $run) {
        foreach ($run['datasets_slowest_first'] as $item) {
            $name = $item['dataset'];
            if (!isset($aggregate[$name])) {
                $aggregate[$name] = [
                    'dataset' => $name,
                    'samples_ms' => [],
                    'rows' => $item['rows'],
                    'failures' => 0,
                    'errors' => [],
                ];
            }
            $aggregate[$name]['samples_ms'][] = (float)$item['elapsed_ms'];
            if ($item['status'] !== 'PASS') {
                $aggregate[$name]['failures']++;
                if ($item['error'] !== null) $aggregate[$name]['errors'][] = $item['error'];
            }
        }
    }

    $summary = [];
    foreach ($aggregate as $item) {
        $samples = $item['samples_ms'];
        sort($samples);
        $count = count($samples);
        $summary[] = [
            'dataset' => $item['dataset'],
            'avg_ms' => round(array_sum($samples) / max(1, $count), 2),
            'min_ms' => round($samples[0] ?? 0, 2),
            'max_ms' => round($samples[$count - 1] ?? 0, 2),
            'rows' => $item['rows'],
            'failures' => $item['failures'],
            'errors' => array_values(array_unique($item['errors'])),
        ];
    }
    usort($summary, function (array $a, array $b): int {
        return $a['avg_ms'] < $b['avg_ms'] ? 1 : -1;
    });

    return [
        'schema' => 'DDB_VISITS_PERFORMANCE_PROFILE_V1',
        'captured_at' => date(DATE_ATOM),
        'hospital' => $hospital,
        'year' => $year,
        'compare_year' => $compareYear,
        'mode' => $mode,
        'from_date' => $fromDate,
        'to_date' => $toDate,
        'iterations' => $iterations,
        'total_profiler_ms' => round((microtime(true) - $overallStarted) * 1000, 2),
        'memory_peak_mb' => round(memory_get_peak_usage(true) / 1048576, 2),
        'summary_slowest_first' => $summary,
        'runs' => $runs,
        'diagnostic_notes' => [
            'Each iteration creates a new VisitsRepository to model a new HTTP request.',
            'The consolidated OPD/ER bundle includes request-level metadata discovery where applicable.',
            'No SQL, page logic, Oracle objects, or business calculations are changed.',
        ],
    ];
}

$hospital = strtolower((string)($argv[1] ?? 'njch'));
$year = (int)($argv[2] ?? date('Y'));
$compareYear = (int)($argv[3] ?? ($year - 1));
$mode = strtolower((string)($argv[4] ?? 'year'));
$fromDate = isset($argv[5]) && $argv[5] !== '' ? (string)$argv[5] : null;
$toDate = isset($argv[6]) && $argv[6] !== '' ? (string)$argv[6] : null;
$iterations = max(1, min(10, (int)($argv[7] ?? 3)));

if ($year === $compareYear) {
    fwrite(STDERR, "Current and comparison years must be different.\n");
    exit(2);
}
if (!in_array($mode, ['year', 'range'], true)) {
    fwrite(STDERR, "Mode must be year or range.\n");
    exit(2);
}
if ($mode === 'range' && ($fromDate === null || $toDate === null)) {
    fwrite(STDERR, "Range mode requires from_date and to_date.\n");
    exit(2);
}

$profile = run_profile($hospital, $year, $compareYear, $mode, $fromDate, $toDate, $iterations);
$dir = dirname(__DIR__) . '/storage/evidence/visits-performance';
if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
    throw new RuntimeException('Cannot create Visits evidence directory.');
}

$stamp = date('Ymd_His');
$jsonFile = $dir . '/VISITS_PERFORMANCE_PROFILE_' . $stamp . '.json';
$csvFile = $dir . '/VISITS_PERFORMANCE_PROFILE_' . $stamp . '.csv';

file_put_contents(
    $jsonFile,
    json_encode($profile, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
    LOCK_EX
);

$csv = fopen($csvFile, 'wb');
fputcsv($csv, ['dataset', 'avg_ms', 'min_ms', 'max_ms', 'rows', 'failures']);
foreach ($profile['summary_slowest_first'] as $row) {
    fputcsv($csv, [
        $row['dataset'],
        $row['avg_ms'],
        $row['min_ms'],
        $row['max_ms'],
        $row['rows'],
        $row['failures'],
    ]);
}
fclose($csv);

file_put_contents(
    $dir . '/LATEST.json',
    json_encode([
        'json' => basename($jsonFile),
        'csv' => basename($csvFile),
        'captured_at' => $profile['captured_at'],
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
    LOCK_EX
);

echo json_encode([
    'status' => 'PASS',
    'json_file' => $jsonFile,
    'csv_file' => $csvFile,
    'total_profiler_ms' => $profile['total_profiler_ms'],
    'summary_slowest_first' => $profile['summary_slowest_first'],
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), PHP_EOL;
