using Sdt.Config;
using Sdt.Core;
using Sdt.Runner;
using Sdt.Tui;
using Spectre.Console;

namespace Sdt.Tui;

public sealed class ToolchainScreen(DevToolConfig config, string projectRoot)
{
    private readonly DevToolConfig _config = config;
    private readonly string _projectRoot = projectRoot;

    public async Task RunAsync()
    {
        while (true)
        {
            AnsiConsole.Clear();
            AnsiConsole.Write(Theme.SectionRule("TOOLCHAINS"));
            AnsiConsole.WriteLine();

            var tc = _config.Toolchains;
            if (tc is null)
            {
                AnsiConsole.MarkupLine(Theme.Warn("No toolchains configured in devtool.json."));
                AnsiConsole.MarkupLine(Theme.Faint("Add a \"toolchains\" section with \"python\" and/or \"node\" entries."));
                AnsiConsole.MarkupLine("\n" + Theme.Faint("Press any key to go back..."));
                Console.ReadKey(intercept: true);
                return;
            }

            // Build menu from available toolchains
            var choices = new List<MenuItem>();

            if (tc.Python is not null)
            {
                choices.Add(new MenuItem($"[bold {Theme.GreenBold}]PYTHON[/]", "__group__"));
                choices.Add(new MenuItem($"{Theme.G("Check environment")}  {Theme.Faint("detect python, venv, pip")}", "py:check"));
                choices.Add(new MenuItem($"{Theme.G("Create / recreate venv")}  {Theme.Faint($"python -m venv {tc.Python.VenvDir}")}", "py:venv"));
                if (tc.Python.Profiles.Count > 0)
                    choices.Add(new MenuItem($"{Theme.G("Install requirements profile")}  {Theme.Faint("select cpu / gpu / nlp...")}", "py:install"));
                choices.Add(new MenuItem($"{Theme.G("Upgrade pip")}  {Theme.Faint("pip install --upgrade pip")}", "py:upgradepip"));
            }

            if (tc.Node is not null)
            {
                choices.Add(new MenuItem($"[bold {Theme.GreenBold}]NODE / NPM[/]", "__group__"));
                choices.Add(new MenuItem($"{Theme.G("Check environment")}  {Theme.Faint("detect node, npm, node_modules")}", "node:check"));
                choices.Add(new MenuItem($"{Theme.G($"{tc.Node.PackageManager} install")}  {Theme.Faint($"in {tc.Node.WorkingDir}")}", "node:install"));
            }

            choices.Add(new MenuItem($"[bold {Theme.GreenBold}]──[/]", "__group__"));
            choices.Add(new MenuItem(Theme.Faint("← Back"), "__back__"));

            var prompt = new SelectionPrompt<MenuItem>()
                .Title($"[{Theme.Green}]Select a toolchain action:[/]")
                .PageSize(20)
                .UseConverter(m => m.Display)
                .AddChoices(choices);

            var selected = AnsiConsole.Prompt(prompt);
            if (selected.Value == "__back__" || selected.Value == "__group__") return;

            AnsiConsole.Clear();
            AnsiConsole.Write(Theme.SectionRule(selected.Value.Split(':')[0].ToUpperInvariant() + " › " + selected.Value.Split(':')[1]));
            AnsiConsole.WriteLine();

            await HandleActionAsync(selected.Value, tc);

            AnsiConsole.MarkupLine("\n" + Theme.Faint("Press any key to continue..."));
            Console.ReadKey(intercept: true);
        }
    }

    // ── Actions ───────────────────────────────────────────────────────────────

    private async Task HandleActionAsync(string action, ToolchainConfig tc)
    {
        switch (action)
        {
            case "py:check": await CheckPythonAsync(tc.Python!); break;
            case "py:venv": await CreateVenvAsync(tc.Python!); break;
            case "py:install": await InstallProfileAsync(tc.Python!); break;
            case "py:upgradepip": await UpgradePipAsync(tc.Python!); break;
            case "node:check": await CheckNodeAsync(tc.Node!); break;
            case "node:install": await NodeInstallAsync(tc.Node!); break;
        }
    }

    // ── Python ────────────────────────────────────────────────────────────────

    private async Task CheckPythonAsync(PythonToolchain py)
    {
        var exe = ResolvePythonExe(py);
        var venvPath = Path.GetFullPath(Path.Combine(_projectRoot, py.VenvDir));
        var venvPython = GetVenvPython(venvPath);

        var table = new Table()
            .Border(TableBorder.Rounded)
            .BorderStyle(Theme.DimStyle)
            .AddColumn(new TableColumn($"[{Theme.Amber}]Check[/]").Width(24))
            .AddColumn(new TableColumn($"[{Theme.GreenBold}]Result[/]"));

        // System Python
        var pyVersion = await ProbeAsync(exe, "--version");
        table.AddRow(Theme.G("System Python"), pyVersion is not null
            ? Theme.Ok(pyVersion.Trim())
            : Theme.Fail($"{exe} not found"));

        // Venv exists?
        table.AddRow(Theme.G($"Venv ({py.VenvDir})"), Directory.Exists(venvPath)
            ? Theme.Ok("exists  " + venvPath)
            : Theme.Warn("not found — use 'Create venv'"));

        // Venv Python
        if (File.Exists(venvPython))
        {
            var venvVersion = await ProbeAsync(venvPython, "--version");
            table.AddRow(Theme.G("Venv Python"), venvVersion is not null
                ? Theme.Ok(venvVersion.Trim())
                : Theme.Fail("could not launch"));
        }

        // Pip in venv
        if (File.Exists(venvPython))
        {
            var pipVersion = await ProbeAsync(venvPython, "-m", "pip", "--version");
            table.AddRow(Theme.G("Pip (venv)"), pipVersion is not null
                ? Theme.Ok(pipVersion.Trim())
                : Theme.Fail("pip not available"));
        }

        AnsiConsole.Write(table);
    }

    private async Task CreateVenvAsync(PythonToolchain py)
    {
        var exe = ResolvePythonExe(py);
        var venvDir = py.VenvDir;
        var venvPath = Path.GetFullPath(Path.Combine(_projectRoot, venvDir));

        if (Directory.Exists(venvPath))
        {
            var overwrite = AnsiConsole.Confirm(
                $"[{Theme.Amber}]Venv already exists at {venvDir}. Recreate it?[/]", defaultValue: false);
            if (!overwrite) return;
            Directory.Delete(venvPath, recursive: true);
        }

        AnsiConsole.MarkupLine(Theme.G($"Creating venv: {exe} -m venv {venvDir}"));
        AnsiConsole.WriteLine();

        await RunLiveAsync(exe, ["-m", "venv", venvDir], _projectRoot);
    }

    private async Task InstallProfileAsync(PythonToolchain py)
    {
        var venvPath = Path.GetFullPath(Path.Combine(_projectRoot, py.VenvDir));
        var venvPy = GetVenvPython(venvPath);

        if (!File.Exists(venvPy))
        {
            AnsiConsole.MarkupLine(Theme.Warn("Venv not found. Create it first."));
            return;
        }

        var profile = AnsiConsole.Prompt(
            new SelectionPrompt<PythonProfile>()
                .Title($"[{Theme.Green}]Select requirements profile:[/]")
                .UseConverter(p => $"{Theme.Bold(p.Label)}  {Theme.Faint(p.RequirementsFile)}")
                .AddChoices(py.Profiles));

        var reqFile = Path.GetFullPath(Path.Combine(_projectRoot, profile.RequirementsFile));
        if (!File.Exists(reqFile))
        {
            AnsiConsole.MarkupLine(Theme.Fail($"Requirements file not found: {reqFile}"));
            return;
        }

        // Upgrade pip first
        AnsiConsole.MarkupLine(Theme.Faint("Upgrading pip..."));
        await RunPipAsync(py, venvPy, ["install", "--upgrade", "pip"]);

        // Build install args
        var installArgs = new List<string> { "-m", "pip", "install" };
        if (!string.IsNullOrWhiteSpace(profile.ExtraIndexUrl))
        {
            installArgs.Add("--extra-index-url");
            installArgs.Add(profile.ExtraIndexUrl);
        }
        installArgs.Add("-r");
        installArgs.Add(reqFile);

        AnsiConsole.WriteLine();
        AnsiConsole.MarkupLine(Theme.G($"Installing {profile.Label}..."));
        AnsiConsole.WriteLine();
        await RunPipAsync(py, venvPy, installArgs.Skip(2)); // strip leading "-m pip"

        // Post-install commands
        foreach (var cmd in profile.PostInstallCommands)
        {
            AnsiConsole.WriteLine();
            AnsiConsole.MarkupLine(Theme.Faint($"Post-install: {cmd}"));
            var parts = cmd.Split(' ', 2);
            var postArgs = parts.Length > 1 ? parts[1].Split(' ') : Array.Empty<string>();
            await RunLiveAsync(venvPy, ["-m", .. postArgs], _projectRoot);
        }
    }

    private async Task UpgradePipAsync(PythonToolchain py)
    {
        var venvPath = Path.GetFullPath(Path.Combine(_projectRoot, py.VenvDir));
        var venvPy = GetVenvPython(venvPath);
        var exe = File.Exists(venvPy) ? venvPy : ResolvePythonExe(py);
        AnsiConsole.MarkupLine(Theme.G($"Upgrading pip using: {exe}"));
        AnsiConsole.WriteLine();
        await RunPipAsync(py, exe, ["install", "--upgrade", "pip"]);
    }

    // ── Node ──────────────────────────────────────────────────────────────────

    private async Task CheckNodeAsync(NodeToolchain node)
    {
        var nodeModules = Path.GetFullPath(
            Path.Combine(_projectRoot, node.WorkingDir, "node_modules"));

        var table = new Table()
            .Border(TableBorder.Rounded)
            .BorderStyle(Theme.DimStyle)
            .AddColumn(new TableColumn($"[{Theme.Amber}]Check[/]").Width(24))
            .AddColumn(new TableColumn($"[{Theme.GreenBold}]Result[/]"));

        var nodeVersion = await ProbeAsync("node", "--version");
        table.AddRow(Theme.G("Node.js"), nodeVersion is not null
            ? Theme.Ok(nodeVersion.Trim())
            : Theme.Fail("node not found in PATH"));

        var npmVersion = await ProbeAsync(node.PackageManager, "--version");
        table.AddRow(Theme.G(node.PackageManager), npmVersion is not null
            ? Theme.Ok(npmVersion.Trim())
            : Theme.Fail($"{node.PackageManager} not found in PATH"));

        table.AddRow(Theme.G("node_modules"), Directory.Exists(nodeModules)
            ? Theme.Ok("exists")
            : Theme.Warn($"not found — run {node.PackageManager} install"));

        AnsiConsole.Write(table);
    }

    private async Task NodeInstallAsync(NodeToolchain node)
    {
        var workDir = Path.GetFullPath(Path.Combine(_projectRoot, node.WorkingDir));
        AnsiConsole.MarkupLine(Theme.G($"{node.PackageManager} install  ({workDir})"));
        AnsiConsole.WriteLine();
        await RunLiveAsync(node.PackageManager, ["install"], workDir);
    }

    // ── Helpers ───────────────────────────────────────────────────────────────

    private static string ResolvePythonExe(PythonToolchain py)
    {
        if (OperatingSystem.IsWindows() && !string.IsNullOrWhiteSpace(py.WindowsExecutable))
            return py.WindowsExecutable;
        return py.Executable;
    }

    private static string GetVenvPython(string venvPath)
    {
        // Windows: .venv\Scripts\python.exe  |  Linux/Mac: .venv/bin/python
        return OperatingSystem.IsWindows()
            ? Path.Combine(venvPath, "Scripts", "python.exe")
            : Path.Combine(venvPath, "bin", "python");
    }

    private static async Task<string?> ProbeAsync(string command, params string[] args)
    {
        try
        {
            var psi = new System.Diagnostics.ProcessStartInfo
            {
                FileName = CommandResolver.Resolve(command),
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true,
            };
            foreach (var a in args) psi.ArgumentList.Add(a);

            using var p = new System.Diagnostics.Process { StartInfo = psi };
            p.Start();
            var output = await p.StandardOutput.ReadToEndAsync();
            var err = await p.StandardError.ReadToEndAsync();
            await p.WaitForExitAsync();
            return p.ExitCode == 0 ? (output + err) : null;
        }
        catch { return null; }
    }

    private static async Task RunLiveAsync(string command, IEnumerable<string> args, string workingDir)
    {
        var result = await ProcessRunner.RunAsync(
            command, args, workingDir,
            (line, isErr) => AnsiConsole.MarkupLine(
                isErr
                    ? $"[{Theme.Amber}]{Markup.Escape(line)}[/]"
                    : $"[{Theme.Green}]{Markup.Escape(line)}[/]"));

        AnsiConsole.WriteLine();
        AnsiConsole.MarkupLine(result.Success
            ? Theme.Ok($"Done ({result.Elapsed.TotalSeconds:F1}s)")
            : Theme.Fail($"Exited {result.ExitCode} ({result.Elapsed.TotalSeconds:F1}s)"));
    }

    private async Task RunPipAsync(PythonToolchain py, string pythonExe, IEnumerable<string> pipArgs)
    {
        if (!string.IsNullOrWhiteSpace(py.PipScript))
        {
            var pipScriptPath = ResolvePipScriptPath(py.PipScript);
            if (File.Exists(pipScriptPath))
            {
                var ext = Path.GetExtension(pipScriptPath).ToLowerInvariant();
                if (ext == ".py")
                {
                    await RunLiveAsync(ResolvePythonExe(py), [pipScriptPath, .. pipArgs], _projectRoot);
                    return;
                }

                AnsiConsole.MarkupLine(Theme.Warn($"Ignoring non-Python pipScript: {pipScriptPath}"));
            }
        }

        await RunLiveAsync(pythonExe, ["-m", "pip", .. pipArgs], _projectRoot);
    }

    private string ResolvePipScriptPath(string pipScriptConfigPath)
    {
        if (Path.IsPathRooted(pipScriptConfigPath))
            return pipScriptConfigPath;

        var fileName = Path.GetFileName(pipScriptConfigPath);
        var bundled = ScriptLocator.FindHelperScript(_projectRoot, fileName);
        if (bundled is not null)
            return bundled;

        return Path.GetFullPath(Path.Combine(_projectRoot, pipScriptConfigPath));
    }
}
