using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Windows;
using AxCopilot.Models;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// $ prefix 핸들러: 클립보드 텍스트 변환
/// </summary>
public class ClipboardHandler : IActionHandler
{
    private readonly SettingsService _settings;
    public string? Prefix => "$";
    public PluginMetadata Metadata => new("clipboard", "클립보드 변환", "1.0", "AX");

    public ClipboardHandler(SettingsService settings) => _settings = settings;

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var items = new List<LauncherItem>();

        // 빌트인 변환 목록
        var builtins = GetBuiltinTransformers()
            .Where(t => string.IsNullOrEmpty(query) ||
                        t.Key.Contains(query, StringComparison.OrdinalIgnoreCase));

        foreach (var t in builtins)
            items.Add(new LauncherItem(t.Key, t.Description ?? "", null, t, Symbol: Symbols.Clipboard));

        // 사용자 정의 변환
        var custom = _settings.Settings.ClipboardTransformers
            .Where(t => string.IsNullOrEmpty(query) ||
                        t.Key.Contains(query, StringComparison.OrdinalIgnoreCase))
            .Select(t => new LauncherItem(t.Key, t.Description ?? t.Type, null, t, Symbol: Symbols.Clipboard));

        items.AddRange(custom);

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is not ClipboardTransformer transformer) return;

        // 클립보드에서 텍스트 읽기 (STA 스레드 필요)
        string? input = null;
        Application.Current.Dispatcher.Invoke(() =>
        {
            input = Clipboard.ContainsText() ? Clipboard.GetText() : null;
        });

        if (input == null) return;

        string? result = await TransformAsync(transformer, input, ct);
        if (result == null) return;

        // 결과를 클립보드에 쓰고 이전 활성 창으로 포커스 복원 후 붙여넣기
        Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(result));

        // 런처 호출 전 활성 창으로 포커스 복원
        var prevHwnd = WindowTracker.PreviousWindow;
        if (prevHwnd != IntPtr.Zero)
            SetForegroundWindow(prevHwnd);

        await Task.Delay(120, ct);
        System.Windows.Forms.SendKeys.SendWait("^v");

        LogService.Info($"클립보드 변환: '{transformer.Key}' 적용");
    }

    private static async Task<string?> TransformAsync(ClipboardTransformer t, string input, CancellationToken ct)
    {
        try
        {
            return t.Type switch
            {
                // ReDoS 방지: 사용자 정의 패턴에 타임아웃 적용
                "regex" when t.Pattern != null && t.Replace != null =>
                    Regex.Replace(input, t.Pattern, t.Replace, RegexOptions.None,
                        TimeSpan.FromMilliseconds(t.Timeout > 0 ? t.Timeout : 5000)),
                "script" when t.Command != null =>
                    await RunScriptAsync(t.Command, input, t.Timeout, ct),
                _ => ExecuteBuiltin(t.Key, input)
            };
        }
        catch (Exception ex)
        {
            LogService.Error($"변환 실패 ({t.Key}): {ex.Message}");
            return null;
        }
    }

    private static async Task<string> RunScriptAsync(string command, string input, int timeoutMs, CancellationToken ct)
    {
        using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        cts.CancelAfter(timeoutMs);

        var parts = command.Split(' ', 2);
        var psi = new ProcessStartInfo(parts[0])
        {
            Arguments = parts.Length > 1 ? parts[1] : "",
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true,
            StandardInputEncoding = Encoding.UTF8,
            StandardOutputEncoding = Encoding.UTF8
        };

        using var proc = Process.Start(psi)!;
        await proc.StandardInput.WriteAsync(input);
        proc.StandardInput.Close();
        return await proc.StandardOutput.ReadToEndAsync(cts.Token);
    }

    // ─── 빌트인 변환 ─────────────────────────────────────────────────────────

    internal static string? ExecuteBuiltin(string key, string input)
    {
        return key switch
        {
            "$json" => FormatJson(input),
            "$upper" => input.ToUpperInvariant(),
            "$lower" => input.ToLowerInvariant(),
            "$ts" => TryParseTimestamp(input),
            "$epoch" => TryParseDate(input),
            "$urle" => Uri.EscapeDataString(input),
            "$urld" => Uri.UnescapeDataString(input),
            "$b64e" => Convert.ToBase64String(Encoding.UTF8.GetBytes(input)),
            "$b64d" => Encoding.UTF8.GetString(Convert.FromBase64String(input)),
            "$md" => StripMarkdown(input),
            "$trim" => input.Trim(),
            "$lines" => string.Join(Environment.NewLine, input.Split('\n').Select(l => l.Trim()).Where(l => l.Length > 0)),
            _ => null
        };
    }

    private static string FormatJson(string input)
    {
        try
        {
            var doc = JsonDocument.Parse(input);
            return JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true });
        }
        catch (Exception) { return input; }
    }

    private static string? TryParseTimestamp(string input)
    {
        if (long.TryParse(input.Trim(), out var ts))
        {
            var dt = DateTimeOffset.FromUnixTimeSeconds(ts).LocalDateTime;
            return dt.ToString("yyyy-MM-dd HH:mm:ss");
        }
        return null;
    }

    private static string? TryParseDate(string input)
    {
        if (DateTime.TryParse(input.Trim(), out var dt))
            return new DateTimeOffset(dt).ToUnixTimeSeconds().ToString();
        return null;
    }

    private static string StripMarkdown(string input) =>
        Regex.Replace(input, @"(\*\*|__)(.*?)\1|(\*|_)(.*?)\3|`(.+?)`|#{1,6}\s*", "$2$4$5");

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    private static IEnumerable<ClipboardTransformer> GetBuiltinTransformers() =>
    [
        new() { Key = "$json",  Type = "builtin", Description = "JSON 포맷팅 (들여쓰기 적용)" },
        new() { Key = "$upper", Type = "builtin", Description = "대문자 변환" },
        new() { Key = "$lower", Type = "builtin", Description = "소문자 변환" },
        new() { Key = "$ts",    Type = "builtin", Description = "유닉스 타임스탬프 → 날짜 문자열" },
        new() { Key = "$epoch", Type = "builtin", Description = "날짜 문자열 → 유닉스 타임스탬프" },
        new() { Key = "$urle",  Type = "builtin", Description = "URL 인코딩" },
        new() { Key = "$urld",  Type = "builtin", Description = "URL 디코딩" },
        new() { Key = "$b64e",  Type = "builtin", Description = "Base64 인코딩" },
        new() { Key = "$b64d",  Type = "builtin", Description = "Base64 디코딩" },
        new() { Key = "$md",    Type = "builtin", Description = "마크다운 문법 제거" },
        new() { Key = "$trim",  Type = "builtin", Description = "앞뒤 공백 제거" },
        new() { Key = "$lines", Type = "builtin", Description = "빈 줄 제거 및 각 줄 공백 정리" },
    ];
}
