using System.IO;
using System.Runtime.InteropServices;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// L5-4: 앱 세션 스냅 핸들러. "session" 프리픽스로 사용합니다.
///
/// 예: session                    → 저장된 세션 목록
///     session 개발환경            → 해당 세션 실행 (앱 + 스냅 레이아웃 적용)
///     session new 이름            → 새 세션 편집기 열기
///     session edit 이름           → 기존 세션 편집기 열기
///     session del 이름            → 세션 삭제
///
/// 세션 = [앱 경로 + 스냅 위치] 목록. 한 번에 모든 앱을 지정 레이아웃으로 실행합니다.
/// </summary>
public class SessionHandler : IActionHandler
{
    private readonly SettingsService _settings;

    public SessionHandler(SettingsService settings) { _settings = settings; }

    public string? Prefix => "session";

    public PluginMetadata Metadata => new(
        "AppSession",
        "앱 세션 스냅 — session",
        "1.0",
        "AX");

    // ─── 항목 목록 ──────────────────────────────────────────────────────────
    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var parts = q.Split(' ', 2, StringSplitOptions.TrimEntries);
        var cmd   = parts.Length > 0 ? parts[0].ToLowerInvariant() : "";

        // "new [이름]" — 새 세션 만들기
        if (cmd == "new")
        {
            var name = parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1]) ? parts[1] : "새 세션";
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem($"'{name}' 세션 만들기",
                    "편집기에서 앱 목록과 스냅 레이아웃을 설정합니다",
                    null, $"__new__{name}", Symbol: "\uE710")
            });
        }

        // "edit 이름" — 기존 세션 편집
        if (cmd == "edit" && parts.Length > 1)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem($"'{parts[1]}' 세션 편집",
                    "편집기 열기",
                    null, $"__edit__{parts[1]}", Symbol: "\uE70F")
            });
        }

        // "del 이름" or "delete 이름" — 세션 삭제
        if ((cmd == "del" || cmd == "delete") && parts.Length > 1)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem($"'{parts[1]}' 세션 삭제",
                    "Enter로 삭제 확인 (되돌릴 수 없습니다)",
                    null, $"__del__{parts[1]}", Symbol: Symbols.Delete)
            });
        }

        // 세션 목록 (필터 적용)
        var sessions = _settings.Settings.AppSessions;
        var filter   = q.ToLowerInvariant();
        var items    = new List<LauncherItem>();

        foreach (var s in sessions)
        {
            if (!string.IsNullOrEmpty(filter) &&
                !s.Name.Contains(filter, StringComparison.OrdinalIgnoreCase) &&
                !s.Description.Contains(filter, StringComparison.OrdinalIgnoreCase))
                continue;

            var appNames = string.Join(", ",
                s.Apps.Take(3).Select(a =>
                    string.IsNullOrEmpty(a.Label)
                        ? Path.GetFileNameWithoutExtension(a.Path)
                        : a.Label));

            items.Add(new LauncherItem(
                s.Name,
                $"{s.Apps.Count}개 앱  ·  {appNames}",
                null, s,
                Symbol: "\uE8A1")); // 창 레이아웃 아이콘
        }

        if (items.Count == 0 && string.IsNullOrEmpty(filter))
        {
            items.Add(new LauncherItem(
                "저장된 세션 없음",
                "'session new 이름'으로 앱 세션을 만드세요",
                null, null,
                Symbol: Symbols.Info));
        }

        // 새 세션 만들기 (항상 표시)
        items.Add(new LauncherItem(
            "새 세션 만들기",
            "session new [이름]  ·  앱 목록 + 스냅 레이아웃 지정",
            null, "__new__새 세션",
            Symbol: "\uE710"));

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

    // ─── 실행 ─────────────────────────────────────────────────────────────
    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is string s)
        {
            if (s.StartsWith("__new__"))
            {
                var name = s["__new__".Length..];
                System.Windows.Application.Current.Dispatcher.Invoke(() =>
                {
                    var win = new Views.SessionEditorWindow(null, _settings);
                    win.InitialName = name;
                    win.Show();
                });
                return;
            }

            if (s.StartsWith("__edit__"))
            {
                var name    = s["__edit__".Length..];
                var session = _settings.Settings.AppSessions
                    .FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
                System.Windows.Application.Current.Dispatcher.Invoke(() =>
                {
                    var win = new Views.SessionEditorWindow(session, _settings);
                    win.Show();
                });
                return;
            }

            if (s.StartsWith("__del__"))
            {
                var name    = s["__del__".Length..];
                var session = _settings.Settings.AppSessions
                    .FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
                if (session != null)
                {
                    _settings.Settings.AppSessions.Remove(session);
                    _settings.Save();
                    NotificationService.Notify("AX Copilot", $"세션 '{name}' 삭제됨");
                }
                return;
            }
        }

        if (item.Data is Models.AppSession appSession)
            await LaunchSessionAsync(appSession, ct);
    }

    // ─── 세션 실행 로직 ───────────────────────────────────────────────────
    private static async Task LaunchSessionAsync(Models.AppSession session, CancellationToken ct)
    {
        NotificationService.Notify("AX Copilot", $"'{session.Name}' 세션 시작...");
        LogService.Info($"세션 실행 시작: {session.Name} ({session.Apps.Count}개 앱)");

        int launched = 0, failed = 0;

        foreach (var app in session.Apps)
        {
            ct.ThrowIfCancellationRequested();

            if (app.DelayMs > 0)
                await Task.Delay(app.DelayMs, ct);

            if (string.IsNullOrWhiteSpace(app.Path)) continue;

            System.Diagnostics.Process? proc = null;
            try
            {
                proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
                {
                    FileName        = app.Path,
                    Arguments       = app.Arguments ?? "",
                    UseShellExecute = true
                });
                launched++;
            }
            catch (Exception ex)
            {
                LogService.Warn($"세션 앱 실행 실패: {app.Path} — {ex.Message}");
                failed++;
                continue;
            }

            // 스냅 위치 없거나 none → 그냥 실행
            if (proc == null || string.IsNullOrEmpty(app.SnapPosition) || app.SnapPosition == "none")
                continue;

            // 창이 나타날 때까지 대기 (최대 6초)
            var hWnd    = IntPtr.Zero;
            var deadline = DateTime.UtcNow.AddSeconds(6);
            while (DateTime.UtcNow < deadline)
            {
                ct.ThrowIfCancellationRequested();
                try { proc.Refresh(); } catch { break; }
                hWnd = proc.MainWindowHandle;
                if (hWnd != IntPtr.Zero) break;
                await Task.Delay(200, ct);
            }

            if (hWnd == IntPtr.Zero)
            {
                LogService.Warn($"세션: 창 핸들 획득 실패 ({app.Path})");
                continue;
            }

            // 창이 완전히 렌더링될 시간 허용
            await Task.Delay(250, ct);
            ApplySnapToWindow(hWnd, app.SnapPosition);
        }

        var msg = failed > 0
            ? $"'{session.Name}' 실행 완료 ({launched}개 성공, {failed}개 실패)"
            : $"'{session.Name}' 실행 완료 ({launched}개 앱)";
        NotificationService.Notify("AX Copilot", msg);
        LogService.Info($"세션 실행 완료: {msg}");
    }

    // ─── 스냅 적용 (SnapHandler와 동일한 좌표 계산) ──────────────────────
    private static void ApplySnapToWindow(IntPtr hwnd, string snapKey)
    {
        if (snapKey == "full")
        {
            ShowWindow(hwnd, SW_MAXIMIZE);
            return;
        }

        var hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        var mi = new MONITORINFO { cbSize = Marshal.SizeOf<MONITORINFO>() };
        if (!GetMonitorInfo(hMonitor, ref mi)) return;

        var w  = mi.rcWork;
        int mw = w.right  - w.left;
        int mh = w.bottom - w.top;
        int mx = w.left;
        int my = w.top;

        var (x, y, cw, ch) = snapKey switch
        {
            "left"    => (mx,              my,               mw / 2,        mh),
            "right"   => (mx + mw / 2,    my,               mw / 2,        mh),
            "top"     => (mx,              my,               mw,            mh / 2),
            "bottom"  => (mx,              my + mh / 2,      mw,            mh / 2),
            "tl"      => (mx,              my,               mw / 2,        mh / 2),
            "tr"      => (mx + mw / 2,    my,               mw / 2,        mh / 2),
            "bl"      => (mx,              my + mh / 2,      mw / 2,        mh / 2),
            "br"      => (mx + mw / 2,    my + mh / 2,      mw / 2,        mh / 2),
            "third-l" => (mx,              my,               mw / 3,        mh),
            "third-c" => (mx + mw / 3,    my,               mw / 3,        mh),
            "third-r" => (mx + mw * 2 / 3,my,               mw / 3,        mh),
            "two3-l"  => (mx,              my,               mw * 2 / 3,    mh),
            "two3-r"  => (mx + mw / 3,    my,               mw * 2 / 3,    mh),
            "center"  => (mx + mw / 10,   my + mh / 10,     mw * 8 / 10,   mh * 8 / 10),
            _         => (mx,              my,               mw,            mh)
        };

        ShowWindow(hwnd, SW_RESTORE);
        SetWindowPos(hwnd, IntPtr.Zero, x, y, cw, ch, SWP_SHOWWINDOW | SWP_NOZORDER);
    }

    // ─── P/Invoke ─────────────────────────────────────────────────────────
    private const uint SWP_SHOWWINDOW = 0x0040;
    private const uint SWP_NOZORDER   = 0x0004;
    private const uint MONITOR_DEFAULTTONEAREST = 0x00000002;
    private const int  SW_RESTORE  = 9;
    private const int  SW_MAXIMIZE = 3;

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT { public int left, top, right, bottom; }

    [StructLayout(LayoutKind.Sequential)]
    private struct MONITORINFO
    {
        public int cbSize;
        public RECT rcMonitor, rcWork;
        public uint dwFlags;
    }

    [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
    [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    [DllImport("user32.dll")] private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
    [DllImport("user32.dll")] private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
}
