using System.CommandLine;
using NatsNet.PortTracker.Data;

namespace NatsNet.PortTracker.Commands;

public static class ModuleCommands
{
    public static Command Create(Option<string> dbOption, Option<string> schemaOption)
    {
        var moduleCommand = new Command("module", "Manage modules");

        // list
        var listStatus = new Option<string?>("--status") { Description = "Filter by status" };
        var listCmd = new Command("list", "List modules");
        listCmd.Add(listStatus);
        listCmd.SetAction(parseResult =>
        {
            var dbPath = parseResult.GetValue(dbOption)!;
            var status = parseResult.GetValue(listStatus);
            using var db = new Database(dbPath);
            var sql = "SELECT id, name, status, go_package, go_line_count, dotnet_project, dotnet_class FROM modules";
            var parameters = new List<(string, object?)>();
            if (status is not null)
            {
                sql += " WHERE status = @status";
                parameters.Add(("@status", status));
            }
            sql += " ORDER BY name";

            var rows = db.Query(sql, parameters.ToArray());
            Console.WriteLine($"{"ID",-5} {"Name",-25} {"Status",-15} {"Go Pkg",-15} {"LOC",-8} {"DotNet Project",-25} {"DotNet Class",-20}");
            Console.WriteLine(new string('-', 113));
            foreach (var row in rows)
            {
                Console.WriteLine($"{row["id"],-5} {row["name"],-25} {row["status"],-15} {row["go_package"],-15} {row["go_line_count"],-8} {row["dotnet_project"] ?? "",-25} {row["dotnet_class"] ?? "",-20}");
            }
            Console.WriteLine($"\nTotal: {rows.Count} modules");
        });

        // show
        var showId = new Argument<int>("id") { Description = "Module ID" };
        var showCmd = new Command("show", "Show module details");
        showCmd.Add(showId);
        showCmd.SetAction(parseResult =>
        {
            var dbPath = parseResult.GetValue(dbOption)!;
            var id = parseResult.GetValue(showId);
            using var db = new Database(dbPath);
            var modules = db.Query("SELECT * FROM modules WHERE id = @id", ("@id", id));
            if (modules.Count == 0)
            {
                Console.WriteLine($"Module {id} not found.");
                return;
            }
            var mod = modules[0];
            Console.WriteLine($"Module #{mod["id"]}: {mod["name"]}");
            Console.WriteLine($"  Status:     {mod["status"]}");
            Console.WriteLine($"  Go Package: {mod["go_package"]}");
            Console.WriteLine($"  Go File:    {mod["go_file"]}");
            Console.WriteLine($"  Go LOC:     {mod["go_line_count"]}");
            Console.WriteLine($"  .NET:       {mod["dotnet_project"]} / {mod["dotnet_namespace"]} / {mod["dotnet_class"]}");
            Console.WriteLine($"  Notes:      {mod["notes"]}");

            var features = db.Query(
                "SELECT id, name, status, go_method, dotnet_method FROM features WHERE module_id = @id ORDER BY name",
                ("@id", id));
            Console.WriteLine($"\n  Features ({features.Count}):");
            foreach (var f in features)
                Console.WriteLine($"    #{f["id"],-5} {f["name"],-35} {f["status"],-15} {f["dotnet_method"] ?? ""}");

            var tests = db.Query(
                "SELECT id, name, status, dotnet_method FROM unit_tests WHERE module_id = @id ORDER BY name",
                ("@id", id));
            Console.WriteLine($"\n  Tests ({tests.Count}):");
            foreach (var t in tests)
                Console.WriteLine($"    #{t["id"],-5} {t["name"],-35} {t["status"],-15} {t["dotnet_method"] ?? ""}");

            var deps = db.Query(
                "SELECT d.target_type, d.target_id, d.dependency_kind, m.name as target_name FROM dependencies d LEFT JOIN modules m ON d.target_type = 'module' AND d.target_id = m.id WHERE d.source_type = 'module' AND d.source_id = @id",
                ("@id", id));
            Console.WriteLine($"\n  Dependencies ({deps.Count}):");
            foreach (var d in deps)
                Console.WriteLine($"    -> {d["target_type"]} #{d["target_id"]} ({d["target_name"]}) [{d["dependency_kind"]}]");
        });

        // update
        var updateId = new Argument<int>("id") { Description = "Module ID" };
        var updateStatus = new Option<string>("--status") { Description = "New status", Required = true };
        var updateCmd = new Command("update", "Update module status");
        updateCmd.Add(updateId);
        updateCmd.Add(updateStatus);
        updateCmd.SetAction(parseResult =>
        {
            var dbPath = parseResult.GetValue(dbOption)!;
            var id = parseResult.GetValue(updateId);
            var status = parseResult.GetValue(updateStatus)!;
            using var db = new Database(dbPath);
            var affected = db.Execute("UPDATE modules SET status = @status WHERE id = @id",
                ("@status", status), ("@id", id));
            Console.WriteLine(affected > 0 ? $"Module {id} updated to '{status}'." : $"Module {id} not found.");
        });

        // map
        var mapId = new Argument<int>("id") { Description = "Module ID" };
        var mapProject = new Option<string>("--project") { Description = "Target .NET project", Required = true };
        var mapNamespace = new Option<string?>("--namespace") { Description = "Target namespace" };
        var mapClass = new Option<string?>("--class") { Description = "Target class" };
        var mapCmd = new Command("map", "Map module to .NET project");
        mapCmd.Add(mapId);
        mapCmd.Add(mapProject);
        mapCmd.Add(mapNamespace);
        mapCmd.Add(mapClass);
        mapCmd.SetAction(parseResult =>
        {
            var dbPath = parseResult.GetValue(dbOption)!;
            var id = parseResult.GetValue(mapId);
            var project = parseResult.GetValue(mapProject)!;
            var ns = parseResult.GetValue(mapNamespace);
            var cls = parseResult.GetValue(mapClass);
            using var db = new Database(dbPath);
            var affected = db.Execute(
                "UPDATE modules SET dotnet_project = @project, dotnet_namespace = @ns, dotnet_class = @cls WHERE id = @id",
                ("@project", project), ("@ns", ns), ("@cls", cls), ("@id", id));
            Console.WriteLine(affected > 0 ? $"Module {id} mapped to {project}." : $"Module {id} not found.");
        });

        // set-na
        var naId = new Argument<int>("id") { Description = "Module ID" };
        var naReason = new Option<string>("--reason") { Description = "Reason for N/A", Required = true };
        var naCmd = new Command("set-na", "Mark module as N/A");
        naCmd.Add(naId);
        naCmd.Add(naReason);
        naCmd.SetAction(parseResult =>
        {
            var dbPath = parseResult.GetValue(dbOption)!;
            var id = parseResult.GetValue(naId);
            var reason = parseResult.GetValue(naReason)!;
            using var db = new Database(dbPath);
            var affected = db.Execute(
                "UPDATE modules SET status = 'n_a', notes = @reason WHERE id = @id",
                ("@reason", reason), ("@id", id));
            Console.WriteLine(affected > 0 ? $"Module {id} set to N/A: {reason}" : $"Module {id} not found.");
        });

        moduleCommand.Add(listCmd);
        moduleCommand.Add(showCmd);
        moduleCommand.Add(updateCmd);
        moduleCommand.Add(mapCmd);
        moduleCommand.Add(naCmd);

        return moduleCommand;
    }
}
