using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using YMhut.Box.Core.Logging;
using YMhut.Box.Core.Settings;
using YMhut.Box.Core.System;

namespace YMhut.Box.Core.Tools;

public interface IBuiltinReferenceToolCatalog
{
    IReadOnlyList<BuiltinReferenceToolModule> GetModules();

    BuiltinReferenceToolDefinition? GetByModuleId(string moduleId);
}

public interface IBuiltinReferenceToolService
{
    Task<ReferenceToolExecutionResult> ExecuteAsync(
        BuiltinReferenceToolDefinition tool,
        bool confirmed = false,
        CancellationToken cancellationToken = default);
}

public sealed class BuiltinReferenceToolCatalog : IBuiltinReferenceToolCatalog
{
    private readonly Lazy<IReadOnlyList<BuiltinReferenceToolModule>> _modules = new(() => Definitions
        .Select(definition => new BuiltinReferenceToolModule(definition))
        .OrderBy(module => module.Metadata.Category)
        .ThenBy(module => module.Metadata.Name, StringComparer.CurrentCulture)
        .ToArray());

    public IReadOnlyList<BuiltinReferenceToolModule> GetModules() => _modules.Value;

    public BuiltinReferenceToolDefinition? GetByModuleId(string moduleId)
    {
        var id = moduleId.StartsWith(BuiltinReferenceToolModule.IdPrefix, StringComparison.OrdinalIgnoreCase)
            ? moduleId[BuiltinReferenceToolModule.IdPrefix.Length..]
            : moduleId;
        return Definitions.FirstOrDefault(definition => string.Equals(definition.Id, id, StringComparison.OrdinalIgnoreCase));
    }

    private static readonly BuiltinReferenceToolDefinition[] Definitions =
    [
        Tool("cert-block", "证书拦截", "管理证书拦截相关入口，执行前要求确认证书写入或删除风险。", ToolCategory.Security, "\uE72E", BuiltinReferenceToolKind.Command, ReferenceToolRiskLevel.High, "打开证书管理", ["cert", "certificate", "block"]),
        Tool("port-viewer", "端口占用", "查看本机 TCP/UDP 端口占用与进程提示。", ToolCategory.Network, "\uE774", BuiltinReferenceToolKind.Command, ReferenceToolRiskLevel.Low, "查看端口占用", ["port", "netstat", "process"]),
        Tool("hosts-editor", "Hosts 编辑", "打开系统 Hosts 文件位置，修改前需要管理员权限和二次确认。", ToolCategory.Network, "\uE779", BuiltinReferenceToolKind.SystemEntry, ReferenceToolRiskLevel.High, "打开 Hosts 文件", ["hosts", "dns", "network"]),
        Tool("keyboard-test", "键盘测试", "打开 YMhut Box 键盘按键测试入口。", ToolCategory.System, "\uE765", BuiltinReferenceToolKind.Information, ReferenceToolRiskLevel.None, "打开测试说明", ["keyboard", "test"]),
        Tool("junk-cleaner", "垃圾清理", "扫描并清理临时文件入口，删除文件前必须再次确认。", ToolCategory.System, "\uE74D", BuiltinReferenceToolKind.Command, ReferenceToolRiskLevel.High, "打开临时目录", ["cleanup", "temp", "delete"]),
        Tool("bsod-analysis", "蓝屏分析", "打开 Minidump 目录并提示使用 WinDbg 或事件查看器分析。", ToolCategory.System, "\uE7BA", BuiltinReferenceToolKind.SystemEntry, ReferenceToolRiskLevel.Low, "打开蓝屏转储目录", ["bsod", "dump", "windbg"]),
        Tool("winget-installer", "Winget 安装", "打开 Winget 包管理入口，安装软件前写入日志并提示确认。", ToolCategory.System, "\uE896", BuiltinReferenceToolKind.Command, ReferenceToolRiskLevel.Medium, "检查 Winget", ["winget", "install", "package"]),
        Tool("battery-report", "电池报告", "生成 Windows battery-report.html 并打开报告。", ToolCategory.System, "\uEBAA", BuiltinReferenceToolKind.Command, ReferenceToolRiskLevel.Low, "生成电池报告", ["battery", "powercfg", "report"]),
        Tool("speed-test", "网速测试", "打开系统网络状态与测速说明。", ToolCategory.Network, "\uE968", BuiltinReferenceToolKind.SystemEntry, ReferenceToolRiskLevel.None, "打开网络状态", ["speed", "network", "test"]),
        Tool("wifi-password", "WiFi 密码", "查看已保存 WiFi 配置需用户确认，敏感输出不会写入日志。", ToolCategory.Security, "\uE701", BuiltinReferenceToolKind.Command, ReferenceToolRiskLevel.High, "列出 WiFi 配置", ["wifi", "password", "wlan"]),
        Tool("disk-space-analyzer", "磁盘分析", "打开 Windows 存储设置并展示磁盘空间分析入口。", ToolCategory.System, "\uEDA2", BuiltinReferenceToolKind.SystemEntry, ReferenceToolRiskLevel.None, "打开存储设置", ["disk", "storage", "space"]),
        Tool("lite-monitor", "硬件监控", "通过 WMI/System.Management 读取硬件摘要，失败时降级展示基础信息。", ToolCategory.System, "\uE9D9", BuiltinReferenceToolKind.Information, ReferenceToolRiskLevel.None, "重新加载硬件摘要", ["hardware", "monitor", "wmi"]),
        Tool("windows-activation", "Windows 激活状态", "仅提供合规入口：查看激活状态、打开系统激活设置和企业 KMS 配置说明。", ToolCategory.System, "\uE895", BuiltinReferenceToolKind.SystemEntry, ReferenceToolRiskLevel.None, "查看激活状态", ["activation", "license", "kms"]),
        Tool("defender-control", "Defender 控制", "打开 Windows 安全中心；修改 Defender 前必须由用户在系统界面确认。", ToolCategory.Security, "\uE72E", BuiltinReferenceToolKind.SystemEntry, ReferenceToolRiskLevel.High, "打开 Windows 安全中心", ["defender", "security"]),
        Tool("cpu-ranking", "CPU 天梯图", "打开随包 Metadata/cpu-ranking.json 数据位置和说明。", ToolCategory.System, "\uEEA1", BuiltinReferenceToolKind.Information, ReferenceToolRiskLevel.None, "查看 CPU 数据", ["cpu", "ranking", "hardware"]),
        Tool("gpu-ranking", "GPU 天梯图", "打开随包 Metadata/gpu-ranking.json 数据位置和说明。", ToolCategory.System, "\uE9D5", BuiltinReferenceToolKind.Information, ReferenceToolRiskLevel.None, "查看 GPU 数据", ["gpu", "ranking", "hardware"]),
        Tool("context-menu-mgr", "右键菜单管理", "打开注册表相关入口，修改右键菜单前必须二次确认。", ToolCategory.System, "\uE8B7", BuiltinReferenceToolKind.SystemEntry, ReferenceToolRiskLevel.High, "打开注册表编辑器", ["context", "menu", "registry"])
    ];

    private static BuiltinReferenceToolDefinition Tool(
        string id,
        string name,
        string description,
        ToolCategory category,
        string glyph,
        BuiltinReferenceToolKind kind,
        ReferenceToolRiskLevel risk,
        string primaryAction,
        IReadOnlyList<string> keywords)
    {
        return new BuiltinReferenceToolDefinition(id, name, description, category, glyph, kind, risk, keywords, primaryAction, []);
    }
}

public sealed class BuiltinReferenceToolService(
    ILogService? logService = null,
    ISettingsService? settingsService = null,
    IHardwareInfoService? hardwareInfoService = null) : IBuiltinReferenceToolService
{
    public async Task<ReferenceToolExecutionResult> ExecuteAsync(
        BuiltinReferenceToolDefinition tool,
        bool confirmed = false,
        CancellationToken cancellationToken = default)
    {
        var start = Stopwatch.StartNew();
        if (tool.IsRisky && !confirmed)
        {
            await WriteLogAsync("Warning", tool.Id, "execute", "confirmation-required", start.ElapsedMilliseconds, null, null, cancellationToken).ConfigureAwait(false);
            return ReferenceToolExecutionResult.ConfirmationRequired("此功能需要二次确认后才会执行。", tool.Description);
        }

        try
        {
            var result = tool.Id switch
            {
                "battery-report" => await GenerateBatteryReportAsync(cancellationToken).ConfigureAwait(false),
                "port-viewer" => await RunCommandCaptureAsync(tool.Id, "cmd.exe", "/c netstat -ano | more", sensitive: false, cancellationToken).ConfigureAwait(false),
                "winget-installer" => await RunCommandCaptureAsync(tool.Id, "winget.exe", "--version", sensitive: false, cancellationToken).ConfigureAwait(false),
                "wifi-password" => await RunCommandCaptureAsync(tool.Id, "netsh.exe", "wlan show profiles", sensitive: true, cancellationToken).ConfigureAwait(false),
                "lite-monitor" => await CaptureHardwareSummaryAsync(cancellationToken).ConfigureAwait(false),
                "windows-activation" => await RunCommandCaptureAsync(tool.Id, "cscript.exe", "//Nologo %windir%\\system32\\slmgr.vbs /xpr", sensitive: false, cancellationToken).ConfigureAwait(false),
                "hosts-editor" => OpenPath(GetHostsPath()),
                "junk-cleaner" => OpenPath(Path.GetTempPath()),
                "bsod-analysis" => OpenPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Minidump")),
                "speed-test" => OpenUri("ms-settings:network-status"),
                "disk-space-analyzer" => OpenUri("ms-settings:storagesense"),
                "defender-control" => OpenUri("windowsdefender:"),
                "context-menu-mgr" => OpenProcess("regedit.exe", string.Empty, true),
                "cert-block" => OpenProcess("certmgr.msc", string.Empty, false),
                "cpu-ranking" => OpenMetadata("cpu-ranking.json"),
                "gpu-ranking" => OpenMetadata("gpu-ranking.json"),
                "keyboard-test" => ReferenceToolExecutionResult.Ok("键盘测试入口已接入。", "请在 YMhut Box 后续键盘测试子页中按键验证；本次打开已记录日志。"),
                _ => ReferenceToolExecutionResult.Fail("暂不支持该内置工具。", tool.Id)
            };

            if (settingsService is not null && result.Success)
            {
                await settingsService.RecordRecentToolAsync(BuiltinReferenceToolModule.IdPrefix + tool.Id, cancellationToken).ConfigureAwait(false);
            }

            await WriteLogAsync(result.Success ? "Information" : result.Cancelled ? "Warning" : "Error", tool.Id, "execute", result.Success ? "success" : result.Cancelled ? "cancelled" : "failed", start.ElapsedMilliseconds, result.Success ? null : result.Message, result.Detail, cancellationToken).ConfigureAwait(false);
            return result;
        }
        catch (Exception exception)
        {
            await WriteLogAsync("Error", tool.Id, "execute", "failed", start.ElapsedMilliseconds, Sanitize(exception.Message), null, cancellationToken).ConfigureAwait(false);
            return ReferenceToolExecutionResult.Fail("内置工具执行失败。", Sanitize(exception.Message));
        }
    }

    private async Task<ReferenceToolExecutionResult> GenerateBatteryReportAsync(CancellationToken cancellationToken)
    {
        var output = Path.Combine(Path.GetTempPath(), $"ymhut-battery-report-{DateTime.Now:yyyyMMdd-HHmmss}.html");
        var result = await RunProcessAsync("powercfg.exe", $"/batteryreport /output \"{output}\"", sensitive: false, cancellationToken).ConfigureAwait(false);
        if (result.ExitCode == 0 && File.Exists(output))
        {
            Process.Start(new ProcessStartInfo { FileName = output, UseShellExecute = true });
            return ReferenceToolExecutionResult.Ok("电池报告已生成并打开。", "报告路径已脱敏记录。", output, result.ExitCode);
        }

        return ReferenceToolExecutionResult.Fail("生成电池报告失败。", result.Output, result.ExitCode);
    }

    private async Task<ReferenceToolExecutionResult> RunCommandCaptureAsync(string toolId, string fileName, string arguments, bool sensitive, CancellationToken cancellationToken)
    {
        var result = await RunProcessAsync(fileName, Environment.ExpandEnvironmentVariables(arguments), sensitive, cancellationToken).ConfigureAwait(false);
        if (result.ExitCode == 0)
        {
            var detail = sensitive ? "命令执行成功；敏感输出未写入日志。" : Trim(result.Output, 2000);
            return ReferenceToolExecutionResult.Ok("命令执行完成。", detail, exitCode: result.ExitCode);
        }

        return ReferenceToolExecutionResult.Fail("命令执行失败。", sensitive ? "敏感输出未写入日志。" : Trim(result.Output, 2000), result.ExitCode);
    }

    private async Task<ReferenceToolExecutionResult> CaptureHardwareSummaryAsync(CancellationToken cancellationToken)
    {
        if (hardwareInfoService is null)
        {
            return ReferenceToolExecutionResult.Fail("硬件信息服务不可用。");
        }

        var summary = await hardwareInfoService.CaptureAsync(cancellationToken).ConfigureAwait(false);
        var text = new StringBuilder()
            .AppendLine($"OS: {summary.OsDescription}")
            .AppendLine($"CPU: {summary.CpuName}")
            .AppendLine($"GPU: {summary.GpuName}")
            .AppendLine($"Memory: {summary.TotalMemory}")
            .AppendLine($"Disk: {summary.PrimaryDisk}")
            .AppendLine($"WMI: {summary.WmiStatus}")
            .ToString();
        return ReferenceToolExecutionResult.Ok("硬件摘要已重新加载。", text);
    }

    private static ReferenceToolExecutionResult OpenMetadata(string fileName)
    {
        var path = ResolveMetadataPath(fileName);
        if (File.Exists(path))
        {
            Process.Start(new ProcessStartInfo { FileName = path, UseShellExecute = true });
            return ReferenceToolExecutionResult.Ok("数据文件已打开。", Path.GetFileName(path), path);
        }

        return ReferenceToolExecutionResult.Fail("未找到随包数据文件。", fileName);
    }

    private static ReferenceToolExecutionResult OpenPath(string path)
    {
        if (!File.Exists(path) && !Directory.Exists(path))
        {
            return ReferenceToolExecutionResult.Fail("目标路径不存在。", path);
        }

        Process.Start(new ProcessStartInfo { FileName = path, UseShellExecute = true });
        return ReferenceToolExecutionResult.Ok("系统入口已打开。", path);
    }

    private static ReferenceToolExecutionResult OpenUri(string uri)
    {
        Process.Start(new ProcessStartInfo { FileName = uri, UseShellExecute = true });
        return ReferenceToolExecutionResult.Ok("系统入口已打开。", uri);
    }

    private static ReferenceToolExecutionResult OpenProcess(string fileName, string arguments, bool runAsAdmin)
    {
        Process.Start(new ProcessStartInfo
        {
            FileName = fileName,
            Arguments = arguments,
            UseShellExecute = true,
            Verb = runAsAdmin ? "runas" : string.Empty
        });
        return ReferenceToolExecutionResult.Ok("系统工具已打开。", fileName);
    }

    private static async Task<(int ExitCode, string Output)> RunProcessAsync(string fileName, string arguments, bool sensitive, CancellationToken cancellationToken)
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = fileName,
                Arguments = arguments,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true,
                StandardOutputEncoding = Encoding.UTF8,
                StandardErrorEncoding = Encoding.UTF8
            }
        };

        process.Start();
        var stdout = await process.StandardOutput.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
        var stderr = await process.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
        await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
        var output = sensitive ? string.Empty : string.Join(Environment.NewLine, [stdout, stderr]).Trim();
        return (process.ExitCode, output);
    }

    private static string GetHostsPath()
    {
        return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers", "etc", "hosts");
    }

    private static string ResolveMetadataPath(string fileName)
    {
        var directory = new DirectoryInfo(AppContext.BaseDirectory);
        while (directory is not null)
        {
            var candidate = Path.Combine(directory.FullName, "Metadata", fileName);
            if (File.Exists(candidate))
            {
                return candidate;
            }

            candidate = Path.Combine(directory.FullName, "tubatool-参考补充项目", "Metadata", fileName);
            if (File.Exists(candidate))
            {
                return candidate;
            }

            directory = directory.Parent;
        }

        return Path.Combine(AppContext.BaseDirectory, "Metadata", fileName);
    }

    private Task WriteLogAsync(
        string level,
        string toolId,
        string operation,
        string result,
        long elapsedMs,
        string? error,
        string? detail,
        CancellationToken cancellationToken)
    {
        var payload = $"toolId={BuiltinReferenceToolModule.IdPrefix}{toolId}; operation={operation}; result={result}; elapsedMs={elapsedMs}; error={error ?? ""}; detail={Sanitize(detail ?? "")}";
        return logService?.WriteAsync(level, "builtin-tool", $"Builtin tool {result}: {toolId}", payload, cancellationToken) ?? Task.CompletedTask;
    }

    private static string Trim(string? value, int length)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        return value.Length > length ? value[..length] : value;
    }

    private static string Sanitize(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        var sanitized = value
            .Replace(Environment.UserName, "%USER%", StringComparison.OrdinalIgnoreCase)
            .Replace(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "%USERPROFILE%", StringComparison.OrdinalIgnoreCase);
        return sanitized.Length > 1200 ? sanitized[..1200] : sanitized;
    }
}
