using System.CommandLine;
using System.Text.Json;
using Spectre.Console;
using VigilCare.Simulation;

public static class MimicGenerateCommand
{
    public static Command Create()
    {
        var dataDirArg = new Argument<DirectoryInfo>("mimic-dir",
            "Path to directory containing MIMIC-IV CSV files");
        var stayIdOpt = new Option<int>("--stay-id", "ICU stay ID to generate scenario for")
        { IsRequired = true };
        var maxHoursOpt = new Option<int?>("--max-hours", "Limit scenario duration in hours");
        var noMedsOpt = new Option<bool>("--no-medications", () => false,
            "Exclude medication events");
        var noLabsOpt = new Option<bool>("--no-labs", () => false,
            "Exclude lab observations");
        var outputOpt = new Option<string?>("--output", "Output file path (default: auto-named)");
        var validateOpt = new Option<bool>("--validate", () => false,
            "Run scenario validation after generation");

        var command = new Command("mimic-generate",
            "Generate a VigilCare scenario JSON from a MIMIC-IV ICU stay")
        {
            dataDirArg, stayIdOpt, maxHoursOpt, noMedsOpt, noLabsOpt, outputOpt, validateOpt
        };

        command.SetHandler(context =>
        {
            var dataDir = context.ParseResult.GetValueForArgument(dataDirArg);
            var stayId = context.ParseResult.GetValueForOption(stayIdOpt);
            var maxHours = context.ParseResult.GetValueForOption(maxHoursOpt);
            var noMeds = context.ParseResult.GetValueForOption(noMedsOpt);
            var noLabs = context.ParseResult.GetValueForOption(noLabsOpt);
            var outputPath = context.ParseResult.GetValueForOption(outputOpt);
            var validate = context.ParseResult.GetValueForOption(validateOpt);

            var loader = new MimicDataLoader(dataDir.FullName);

            AnsiConsole.MarkupLine($"[bold]Loading MIMIC-IV data for stay {stayId}...[/]");

            var stays = loader.LoadIcuStays();
            var stay = stays.FirstOrDefault(s => s.StayId == stayId);
            if (stay is null)
            {
                AnsiConsole.MarkupLine($"[red]ICU stay {stayId} not found.[/]");
                var available = stays.Select(s => s.StayId).OrderBy(x => x).ToList();
                AnsiConsole.MarkupLine($"Available stay IDs: {string.Join(", ", available.Take(20))}...");
                return;
            }

            var admissions = loader.LoadAdmissions();
            var admission = admissions.FirstOrDefault(a => a.HadmId == stay.HadmId);
            if (admission is null)
            {
                AnsiConsole.MarkupLine($"[red]Admission {stay.HadmId} not found.[/]");
                return;
            }

            var patients = loader.LoadPatients();
            var patient = patients.FirstOrDefault(p => p.SubjectId == stay.SubjectId);
            if (patient is null)
            {
                AnsiConsole.MarkupLine($"[red]Patient {stay.SubjectId} not found.[/]");
                return;
            }

            AnsiConsole.MarkupLine(
                $"  Patient: [cyan]{patient.SubjectId}[/] ({patient.Gender}, ~{patient.AnchorAge}y)");
            AnsiConsole.MarkupLine(
                $"  Stay:    [cyan]{stay.StayId}[/] in {stay.FirstCareUnit}");
            AnsiConsole.MarkupLine(
                $"  LOS:     {stay.Los:F1} days ({stay.InTime:g} → {stay.OutTime:g})");
            AnsiConsole.MarkupLine(
                $"  Outcome: {(admission.HospitalExpireFlag == 1 ? "[red]Expired[/]" : "Survived")}");

            var options = new MimicGenerateOptions(
                MaxHours: maxHours,
                IncludeMedications: !noMeds,
                IncludeLabs: !noLabs);

            AnsiConsole.MarkupLine("\n[bold]Generating scenario...[/]");

            var builder = new MimicScenarioBuilder(loader);
            var (scenario, warnings) = builder.Build(stay, admission, patient, options);

            foreach (var warning in warnings)
                AnsiConsole.MarkupLine($"  [yellow]WARNING:[/] {Markup.Escape(warning)}");

            var obsCount = scenario.Events.Count(e => e.Type == "observation");
            var medCount = scenario.Events.Count(e => e.Type == "medication");
            AnsiConsole.MarkupLine(
                $"  Events: [green]{obsCount}[/] observations, [green]{medCount}[/] medications");
            AnsiConsole.MarkupLine(
                $"  Duration: {scenario.Scenario.DurationMinutes} minutes " +
                $"({scenario.Scenario.DurationMinutes / 60.0:F1} hours)");

            if (validate)
            {
                var errors = ScenarioValidator.Validate(scenario);
                if (errors.Count == 0)
                {
                    AnsiConsole.MarkupLine("  [green]Validation: PASSED[/]");
                }
                else
                {
                    AnsiConsole.MarkupLine($"  [red]Validation: {errors.Count} error(s)[/]");
                    foreach (var err in errors)
                        AnsiConsole.MarkupLine($"    [red]• {Markup.Escape(err)}[/]");
                }
            }

            var jsonOptions = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                WriteIndented = true,
                Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
            };

            var json = JsonSerializer.Serialize(scenario, jsonOptions);

            var filePath = outputPath
                ?? Path.Combine("Scenarios", "List", $"mimic-s{stayId}.json");

            var dir = Path.GetDirectoryName(filePath);
            if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            File.WriteAllText(filePath, json);
            AnsiConsole.MarkupLine($"\n[bold green]Scenario written to:[/] {filePath}");
            AnsiConsole.MarkupLine($"  Replay with: [dim]dotnet run -- replay {filePath}[/]");
        });

        return command;
    }
}
