using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using AxCopilot.Services;
using AxCopilot.ViewModels;

namespace AxCopilot.Views;

public partial class SettingsWindow : Window
{
    private readonly SettingsViewModel _vm;
    private readonly Action<string> _previewCallback;
    private readonly Action _revertCallback;
    private bool _saved;
    /// <summary>App 인스턴스 캐시 — <c>Application.Current as App</c> 반복 호출 제거.</summary>
    private static App? CurrentApp => System.Windows.Application.Current as App;

    /// <summary>
    /// 핫키 녹화 시작/종료를 외부(App.xaml.cs)에 알리는 콜백.
    /// true = 녹화 시작(핫키 일시 정지), false = 녹화 종료(핫키 재개)
    /// </summary>
    public Action<bool>? SuspendHotkeyCallback { get; set; }

    /// <param name="previewCallback">테마 키를 받아 런처에 즉시 미리보기 적용</param>
    /// <param name="revertCallback">취소/X 닫기 시 원래 설정으로 복원</param>
    public SettingsWindow(SettingsViewModel vm, Action<string> previewCallback, Action revertCallback)
    {
        InitializeComponent();
        _vm = vm;
        _previewCallback = previewCallback;
        _revertCallback = revertCallback;
        DataContext = vm;

        vm.ThemePreviewRequested += (_, _) => _previewCallback(vm.SelectedThemeKey);
        vm.SaveCompleted += (_, _) =>
        {
            _saved = true;
            Close();
            // 인덱스 재빌드를 백그라운드에서 조용히 실행 (UI 차단 없음)
            _ = Task.Run(async () =>
            {
                try
                {
                    var indexService = CurrentApp?.IndexService;
                    if (indexService != null)
                        await indexService.BuildAsync();
                }
                catch (Exception) { /* 인덱싱 실패해도 설정 저장은 완료 */ }
            });
        };

        Loaded += async (_, _) =>
        {
            RefreshHotkeyBadges();
            SetVersionText();
            EnsureHotkeyInCombo();
            BuildQuoteCategoryCheckboxes();
            BuildDockBarSettings();
            BuildTextActionCommandsPanel();
            MoveBlockSectionToEtc();

            // 스킬이 아직 로드되지 않았으면 백그라운드에서 로드 후 UI 구성
            var app = CurrentApp;
            var skillsLoaded = Services.Agent.SkillService.Skills.Count > 0;
            if (!skillsLoaded && (app?.SettingsService?.Settings.Llm.EnableSkillSystem ?? false))
            {
                // 스킬 로드 (RuntimeDetector 포함)를 백그라운드에서 실행
                await Task.Run(() =>
                {
                    Services.Agent.SkillService.EnsureSkillFolder();
                    Services.Agent.SkillService.LoadSkills(app?.SettingsService?.Settings.Llm.SkillsFolderPath);
                });
            }

            BuildToolRegistryPanel();
            LoadAdvancedSettings();
            RefreshStorageInfo();
            InitPluginGallery();  // Phase 18-C1: 플러그인 갤러리 초기화
            // 개발자 모드는 저장된 설정 유지 (끄면 하위 기능 모두 비활성)
            UpdateDevModeContentVisibility();
            // AI 기능 토글 초기화
            ApplyAiEnabledState(app?.SettingsService?.Settings.AiEnabled ?? false, init: true);
            // 네트워크 모드 토글 초기화 (사내 모드 = true = 차단)
            if (InternalModeToggle != null)
                InternalModeToggle.IsChecked = app?.SettingsService?.Settings.InternalModeEnabled ?? true;
        };
    }

    // ─── 에이전트 차단 섹션 → 기타 탭 이동 ──────────────────────────────────────

    private void MoveBlockSectionToEtc()
    {
        if (AgentBlockSection == null || AgentEtcContent == null) return;
        var parent = AgentBlockSection.Parent as Panel;
        if (parent != null)
        {
            parent.Children.Remove(AgentBlockSection);
            AgentBlockSection.Margin = new Thickness(0, 0, 0, 16);
            AgentEtcContent.Children.Add(AgentBlockSection);
        }
    }

    private void AddSnippet_Click(object sender, RoutedEventArgs e)
    {
        if (!_vm.AddSnippet())
            CustomMessageBox.Show(
                "키워드와 내용은 필수 항목입니다.\n동일한 키워드가 이미 존재하면 추가할 수 없습니다.",
                "입력 오류", MessageBoxButton.OK, MessageBoxImage.Warning);
    }

    private void DeleteSnippet_Click(object sender, RoutedEventArgs e)
    {
        if (sender is Button btn && btn.Tag is ViewModels.SnippetRowModel row)
            _vm.RemoveSnippet(row);
    }

    private void Browse_Click(object sender, RoutedEventArgs e) => _vm.BrowseTarget();

    private void AddShortcut_Click(object sender, RoutedEventArgs e)
    {
        if (!_vm.AddShortcut())
            CustomMessageBox.Show(
                "키워드와 실행 대상은 필수 항목입니다.\n이미 동일한 키워드가 존재하면 추가할 수 없습니다.",
                "입력 오류", MessageBoxButton.OK, MessageBoxImage.Warning);
    }

    private void DeleteShortcut_Click(object sender, RoutedEventArgs e)
    {
        if (sender is Button btn && btn.Tag is ViewModels.AppShortcutModel shortcut)
            _vm.RemoveShortcut(shortcut);
    }

    // ─── 전용 핫키 이벤트 핸들러 ───────────────────────────────────────────────

    /// <summary>핫키 입력 영역 클릭 → PreviewKeyDown 캡처 모드 활성화</summary>
    private bool _hotkeyRecording;

    private void HotkeyRecord_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        _hotkeyRecording = true;
        SuspendHotkeyCallback?.Invoke(true);
        _vm.NewHotkeyStr = "키를 누르세요…";
        // 전역 PreviewKeyDown 캡처
        this.PreviewKeyDown -= HotkeyGlobalCapture_PreviewKeyDown;
        this.PreviewKeyDown += HotkeyGlobalCapture_PreviewKeyDown;
    }

    private void HotkeyGlobalCapture_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (!_hotkeyRecording) return;
        e.Handled = true;

        var key = e.Key == System.Windows.Input.Key.System ? e.SystemKey : e.Key;

        // 수정자 단독 → 무시
        if (key is System.Windows.Input.Key.LeftCtrl or System.Windows.Input.Key.RightCtrl
            or System.Windows.Input.Key.LeftAlt or System.Windows.Input.Key.RightAlt
            or System.Windows.Input.Key.LeftShift or System.Windows.Input.Key.RightShift
            or System.Windows.Input.Key.LWin or System.Windows.Input.Key.RWin)
            return;

        if (key == System.Windows.Input.Key.Escape)
        {
            _vm.NewHotkeyStr = "";
            _hotkeyRecording = false;
            SuspendHotkeyCallback?.Invoke(false);
            this.PreviewKeyDown -= HotkeyGlobalCapture_PreviewKeyDown;
            return;
        }

        var parts = new List<string>();
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Control)) parts.Add("Ctrl");
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Alt))     parts.Add("Alt");
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Shift))   parts.Add("Shift");
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Windows)) parts.Add("Win");

        var keyName = key switch
        {
            System.Windows.Input.Key.Snapshot => "PrintScreen",
            System.Windows.Input.Key.Scroll   => "ScrollLock",
            System.Windows.Input.Key.Pause    => "Pause",
            _ => key.ToString()
        };
        parts.Add(keyName);

        var hotkey = string.Join("+", parts);
        if (Core.HotkeyParser.TryParse(hotkey, out _))
            _vm.NewHotkeyStr = hotkey;

        _hotkeyRecording = false;
        SuspendHotkeyCallback?.Invoke(false);
        this.PreviewKeyDown -= HotkeyGlobalCapture_PreviewKeyDown;
    }

    private void AddHotkey_Click(object sender, RoutedEventArgs e)
    {
        _vm.AddCustomHotkey();
    }

    private void RemoveHotkey_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (sender is FrameworkElement fe && fe.Tag is ViewModels.HotkeyRowModel row)
            _vm.RemoveCustomHotkey(row);
    }

    private void BrowseHotkeyTarget_Click(object sender, RoutedEventArgs e)
    {
        var dlg = new Microsoft.Win32.OpenFileDialog
        {
            Title  = "실행 파일 선택",
            Filter = "실행 파일|*.exe;*.bat;*.cmd;*.ps1;*.lnk|모든 파일|*.*"
        };
        if (dlg.ShowDialog(this) == true)
        {
            _vm.NewHotkeyTarget = dlg.FileName;
            _vm.NewHotkeyType   = "app";
            if (string.IsNullOrWhiteSpace(_vm.NewHotkeyLabel))
                _vm.NewHotkeyLabel = System.IO.Path.GetFileNameWithoutExtension(dlg.FileName);
        }
    }

    private void AddBatchCommand_Click(object sender, RoutedEventArgs e)
    {
        if (!_vm.AddBatchCommand())
            CustomMessageBox.Show(
                "키워드와 명령어는 필수 항목입니다.\n동일한 키워드가 이미 존재하면 추가할 수 없습니다.",
                "입력 오류", MessageBoxButton.OK, MessageBoxImage.Warning);
    }

    private void DeleteBatchCommand_Click(object sender, RoutedEventArgs e)
    {
        if (sender is Button btn && btn.Tag is ViewModels.BatchCommandModel cmd)
            _vm.RemoveBatchCommand(cmd);
    }

    private void ResetCommandAliases_Click(object sender, RoutedEventArgs e)
    {
        _vm.ResetSystemCommandAliases();
    }

    private void AddIndexPath_Click(object sender, RoutedEventArgs e)
    {
        var text = NewIndexPathBox?.Text?.Trim() ?? "";
        if (!string.IsNullOrWhiteSpace(text))
        {
            _vm.AddIndexPath(text);
            if (NewIndexPathBox != null) NewIndexPathBox.Text = "";
        }
    }

    private void BrowseIndexPath_Click(object sender, RoutedEventArgs e) => _vm.BrowseIndexPath();

    private void ResetCapPrefix_Click(object sender, RoutedEventArgs e) => _vm.ResetCapPrefix();

    private void ResetCapGlobalHotkey_Click(object sender, RoutedEventArgs e) => _vm.ResetCapGlobalHotkey();

    private void CapHotkeyRecorder_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        e.Handled = true;
        var key = e.Key == System.Windows.Input.Key.System ? e.SystemKey : e.Key;

        // 수정자만 입력된 경우 무시
        if (key is System.Windows.Input.Key.LeftCtrl or System.Windows.Input.Key.RightCtrl
            or System.Windows.Input.Key.LeftAlt or System.Windows.Input.Key.RightAlt
            or System.Windows.Input.Key.LeftShift or System.Windows.Input.Key.RightShift
            or System.Windows.Input.Key.LWin or System.Windows.Input.Key.RWin)
            return;

        if (key == System.Windows.Input.Key.Escape) return;

        var parts = new List<string>();
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Control)) parts.Add("Ctrl");
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Alt)) parts.Add("Alt");
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Shift)) parts.Add("Shift");
        if (System.Windows.Input.Keyboard.Modifiers.HasFlag(System.Windows.Input.ModifierKeys.Windows)) parts.Add("Win");

        var keyName = key switch
        {
            System.Windows.Input.Key.Snapshot => "PrintScreen",
            System.Windows.Input.Key.Scroll   => "ScrollLock",
            System.Windows.Input.Key.Pause    => "Pause",
            _ => key.ToString()
        };
        parts.Add(keyName);

        var hotkey = string.Join("+", parts);
        if (AxCopilot.Core.HotkeyParser.TryParse(hotkey, out _))
            _vm.CapGlobalHotkey = hotkey;
    }

    private void ReminderCorner_Click(object sender, RoutedEventArgs e)
    {
        if (sender is System.Windows.Controls.RadioButton rb && rb.Tag is string corner)
            _vm.ReminderCorner = corner;
    }

    private void ReminderPreview_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var svc = _vm.Service;
            var (text, author) = Services.QuoteService.GetRandom(svc.Settings.Reminder.EnabledCategories);
            var usage = TimeSpan.FromMinutes(42); // 샘플 값

            // 현재 편집 중인 값을 임시로 적용
            var cfg = svc.Settings.Reminder;
            var origCorner  = cfg.Corner;
            var origSeconds = cfg.DisplaySeconds;
            cfg.Corner         = _vm.ReminderCorner;
            cfg.DisplaySeconds = _vm.ReminderDisplaySeconds;

            var popup = new ReminderPopupWindow(text, author, usage, svc);
            popup.Show();

            cfg.Corner         = origCorner;
            cfg.DisplaySeconds = origSeconds;
        }
        catch (Exception ex)
        {
            Services.LogService.Warn($"알림 미리보기 실패: {ex.Message}");
        }
    }

    private void RemoveIndexPath_Click(object sender, RoutedEventArgs e)
    {
        if (sender is Button btn && btn.Tag is string path)
            _vm.RemoveIndexPath(path);
    }

    private void AddExtension_Click(object sender, RoutedEventArgs e)
    {
        var text = NewExtensionBox?.Text?.Trim() ?? "";
        if (!string.IsNullOrWhiteSpace(text))
        {
            _vm.AddExtension(text);
            if (NewExtensionBox != null) NewExtensionBox.Text = "";
        }
    }

    private void RemoveExtension_Click(object sender, RoutedEventArgs e)
    {
        if (sender is Button btn && btn.Tag is string ext)
            _vm.RemoveExtension(ext);
    }

    private void Save_Click(object sender, RoutedEventArgs e)
    {
        SaveAdvancedSettings(); // 폴백/MCP 텍스트 박스 → settings 저장
        _vm.Save();
        CustomMessageBox.Show("설정이 저장되었습니다.", "AX Copilot", MessageBoxButton.OK, MessageBoxImage.Information);
    }

    private void Cancel_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }

    private void ExportSettings_Click(object sender, RoutedEventArgs e)
    {
        var dlg = new Microsoft.Win32.SaveFileDialog
        {
            Title      = "설정 내보내기",
            Filter     = "AX Copilot 설정|*.axsettings",
            FileName   = $"AxCopilot_Settings_{DateTime.Now:yyyyMMdd}",
            DefaultExt = ".axsettings"
        };
        if (dlg.ShowDialog() != true) return;

        try
        {
            var srcPath = SettingsService.SettingsPath;
            if (System.IO.File.Exists(srcPath))
            {
                System.IO.File.Copy(srcPath, dlg.FileName, overwrite: true);
                CustomMessageBox.Show(
                    $"설정이 내보내졌습니다:\n{dlg.FileName}",
                    "AX Copilot", MessageBoxButton.OK,
                    MessageBoxImage.Information);
            }
        }
        catch (Exception ex)
        {
            CustomMessageBox.Show($"내보내기 실패: {ex.Message}", "오류");
        }
    }

    private void ImportSettings_Click(object sender, RoutedEventArgs e)
    {
        var dlg = new Microsoft.Win32.OpenFileDialog
        {
            Title  = "설정 불러오기",
            Filter = "AX Copilot 설정|*.axsettings|모든 파일|*.*"
        };
        if (dlg.ShowDialog() != true) return;

        var result = CustomMessageBox.Show(
            "설정을 불러오면 현재 설정이 덮어씌워집니다.\n계속하시겠습니까?",
            "AX Copilot — 설정 불러오기",
            MessageBoxButton.YesNo,
            MessageBoxImage.Question);

        if (result != System.Windows.MessageBoxResult.Yes) return;

        try
        {
            var fileContent = System.IO.File.ReadAllText(dlg.FileName);
            // 암호화된 파일인지 판별: PortableDecrypt 시도
            var json = CryptoService.PortableDecrypt(fileContent);
            if (string.IsNullOrEmpty(json))
            {
                // 평문 JSON일 수 있음 — 직접 파싱 시도
                try
                {
                    var test = System.Text.Json.JsonSerializer.Deserialize<Models.AppSettings>(fileContent);
                    if (test != null) json = fileContent;
                }
                catch (Exception) { }
            }

            if (string.IsNullOrEmpty(json))
            {
                CustomMessageBox.Show("유효하지 않은 설정 파일입니다.", "오류");
                return;
            }

            // 유효한 설정인지 최종 확인
            var settings = System.Text.Json.JsonSerializer.Deserialize<Models.AppSettings>(json);
            if (settings == null)
            {
                CustomMessageBox.Show("설정 파일을 파싱할 수 없습니다.", "오류");
                return;
            }

            // 암호화하여 저장
            var encrypted = CryptoService.PortableEncrypt(json);
            System.IO.File.WriteAllText(SettingsService.SettingsPath, encrypted);

            CustomMessageBox.Show(
                "설정이 불러와졌습니다.\n변경 사항을 적용하려면 앱을 재시작하세요.",
                "AX Copilot", MessageBoxButton.OK,
                MessageBoxImage.Information);
            Close();
        }
        catch (Exception ex)
        {
            CustomMessageBox.Show($"불러오기 실패: {ex.Message}", "오류");
        }
    }

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
        // 저장하지 않고 닫아도 확인 없이 바로 닫힘 (revert는 OnClosed에서 처리)
        base.OnClosing(e);
    }

    protected override void OnClosed(EventArgs e)
    {
        if (!_saved) _revertCallback?.Invoke();
        base.OnClosed(e);
    }
}
