using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using AxCopilot.Models;
using AxCopilot.Services.Agent;

namespace AxCopilot.Views;

public partial class ChatWindow
{
    // ─── 메시지 우클릭 컨텍스트 메뉴 ───────────────────────────────────────

    private void ShowMessageContextMenu(string content, string role)
    {
        var menu = CreateThemedContextMenu();
        var primaryText = ThemeResourceHelper.Primary(this);
        var secondaryText = ThemeResourceHelper.Secondary(this);

        void AddItem(string icon, string label, Action action)
        {
            var sp = new StackPanel { Orientation = Orientation.Horizontal };
            sp.Children.Add(new TextBlock
            {
                Text = icon, FontFamily = ThemeResourceHelper.SegoeMdl2,
                FontSize = 12, Foreground = secondaryText,
                VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 8, 0),
            });
            sp.Children.Add(new TextBlock
            {
                Text = label, FontSize = 12, Foreground = primaryText,
                VerticalAlignment = VerticalAlignment.Center,
            });
            var mi = new MenuItem { Header = sp, Padding = new Thickness(8, 6, 16, 6) };
            mi.Click += (_, _) => action();
            menu.Items.Add(mi);
        }

        // 복사
        AddItem("\uE8C8", "텍스트 복사", () =>
        {
            try { Clipboard.SetText(content); ShowToast("복사되었습니다"); } catch (Exception) { /* 클립보드 접근 실패 */ }
        });

        // 마크다운 복사
        AddItem("\uE943", "마크다운 복사", () =>
        {
            try { Clipboard.SetText(content); ShowToast("마크다운으로 복사됨"); } catch (Exception) { /* 클립보드 접근 실패 */ }
        });

        // 인용하여 답장
        AddItem("\uE97A", "인용하여 답장", () =>
        {
            var quote = content.Length > 200 ? content[..200] + "..." : content;
            var lines = quote.Split('\n');
            var quoted = string.Join("\n", lines.Select(l => $"> {l}"));
            InputBox.Text = quoted + "\n\n";
            InputBox.Focus();
            InputBox.CaretIndex = InputBox.Text.Length;
        });

        menu.Items.Add(new Separator());

        // 재생성 (AI 응답만)
        if (role == "assistant")
        {
            AddItem("\uE72C", "응답 재생성", () => _ = RegenerateLastAsync());
        }

        // 대화 분기 (Fork)
        AddItem("\uE8A5", "여기서 분기", () =>
        {
            ChatConversation? conv;
            lock (_convLock) conv = _currentConversation;
            if (conv == null) return;

            var idx = conv.Messages.FindLastIndex(m => m.Role == role && m.Content == content);
            if (idx < 0) return;

            ForkConversation(conv, idx);
        });

        menu.Items.Add(new Separator());

        // 이후 메시지 모두 삭제
        var msgContent = content;
        var msgRole = role;
        AddItem("\uE74D", "이후 메시지 모두 삭제", () =>
        {
            ChatConversation? conv;
            lock (_convLock) conv = _currentConversation;
            if (conv == null) return;

            var idx = conv.Messages.FindLastIndex(m => m.Role == msgRole && m.Content == msgContent);
            if (idx < 0) return;

            var removeCount = conv.Messages.Count - idx;
            if (MessageBox.Show($"이 메시지 포함 {removeCount}개 메시지를 삭제하시겠습니까?",
                    "메시지 삭제", MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
                return;

            conv.Messages.RemoveRange(idx, removeCount);
            try { _storage.Save(conv); } catch (Exception ex) { Services.LogService.Debug($"대화 저장 실패: {ex.Message}"); }
            RenderMessages();
            ShowToast($"{removeCount}개 메시지 삭제됨");
        });

        menu.IsOpen = true;
    }

    // ─── 팁 알림 ──────────────────────────────────────────────────────

    private static readonly string[] Tips =
    [
        "💡 작업 폴더에 AX.md 파일을 만들면 매번 시스템 프롬프트에 자동 주입됩니다. 프로젝트 설계 원칙이나 코딩 규칙을 기록하세요.",
        "💡 Ctrl+1/2/3으로 Chat/Cowork/Code 탭을 빠르게 전환할 수 있습니다.",
        "💡 Ctrl+F로 현재 대화 내 메시지를 검색할 수 있습니다.",
        "💡 메시지를 우클릭하면 복사, 인용 답장, 재생성, 삭제를 할 수 있습니다.",
        "💡 코드 블록을 더블클릭하면 전체화면으로 볼 수 있고, 💾 버튼으로 파일 저장이 가능합니다.",
        "💡 Cowork 에이전트가 만든 파일은 자동으로 날짜_시간 접미사가 붙어 덮어쓰기를 방지합니다.",
        "💡 Code 탭에서 개발 언어를 선택하면 해당 언어 우선으로 코드를 생성합니다.",
        "💡 파일 탐색기(하단 바 '파일' 버튼)에서 더블클릭으로 프리뷰, 우클릭으로 관리할 수 있습니다.",
        "💡 에이전트가 계획을 제시하면 '수정 요청'으로 방향을 바꾸거나 '취소'로 중단할 수 있습니다.",
        "💡 Code 탭은 빌드/테스트를 자동으로 실행합니다. 프로젝트 폴더를 먼저 선택하세요.",
        "💡 무드 갤러리에서 10가지 디자인 템플릿 중 원하는 스타일을 미리보기로 선택할 수 있습니다.",
        "💡 Git 연동: Code 탭에서 에이전트가 git status, diff, commit을 수행합니다. (push는 직접)",
        "💡 설정 → AX Agent → 공통에서 개발자 모드를 켜면 에이전트 동작을 스텝별로 검증할 수 있습니다.",
        "💡 트레이 아이콘 우클릭 → '사용 통계'에서 대화 빈도와 토큰 사용량을 확인할 수 있습니다.",
        "💡 대화 제목을 클릭하면 이름을 변경할 수 있습니다.",
        "💡 LLM 오류 발생 시 '재시도' 버튼이 자동으로 나타납니다.",
        "💡 검색란에서 대화 제목뿐 아니라 첫 메시지 내용까지 검색됩니다.",
        "💡 프리셋 선택 후에도 대화가 리셋되지 않습니다. 진행 중인 대화에서 프리셋을 변경할 수 있습니다.",
        "💡 Shift+Enter로 퍼지 검색 결과의 파일이 있는 폴더를 열 수 있습니다.",
        "💡 최근 폴더를 우클릭하면 '폴더 열기', '경로 복사', '목록에서 삭제'가 가능합니다.",
        "💡 Cowork/Code 에이전트 작업 완료 시 시스템 트레이에 알림이 표시됩니다.",
        "💡 마크다운 테이블, 인용(>), 취소선(~~), 링크([text](url))가 모두 렌더링됩니다.",
        "💡 ⚠ 데이터 폴더를 워크스페이스로 지정할 때는 반드시 백업을 먼저 만드세요!",
        "💡 드라이브 루트(C:\\, D:\\)는 작업공간으로 설정할 수 없습니다. 하위 폴더를 선택하세요.",
    ];
    private int _tipIndex;
    private DispatcherTimer? _tipDismissTimer;

    private void ShowRandomTip()
    {
        if (!Llm.ShowTips) return;
        if (_activeTab != "Cowork" && _activeTab != "Code") return;

        var tip = Tips[_tipIndex % Tips.Length];
        _tipIndex++;

        // 토스트 스타일로 표시 (기존 토스트와 다른 위치/색상)
        ShowTip(tip);
    }

    private void ShowTip(string message)
    {
        _tipDismissTimer?.Stop();

        ToastText.Text = message;
        ToastIcon.Text = "\uE82F"; // 전구 아이콘
        ToastBorder.Visibility = Visibility.Visible;
        ToastBorder.BeginAnimation(UIElement.OpacityProperty,
            new System.Windows.Media.Animation.DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(300)));

        var duration = Llm.TipDurationSeconds;
        if (duration <= 0) return; // 0이면 수동 닫기 (자동 사라짐 없음)

        _tipDismissTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(duration) };
        _tipDismissTimer.Tick += (_, _) =>
        {
            _tipDismissTimer.Stop();
            var fadeOut = new System.Windows.Media.Animation.DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(300));
            fadeOut.Completed += (_, _) => ToastBorder.Visibility = Visibility.Collapsed;
            ToastBorder.BeginAnimation(UIElement.OpacityProperty, fadeOut);
        };
        _tipDismissTimer.Start();
    }

    // ─── 프로젝트 문맥 파일 (AX.md) ──────────────────────────────────

    /// <summary>
    /// 작업 폴더에 AX.md가 있으면 내용을 읽어 시스템 프롬프트에 주입합니다.
    /// Claude Code와 동일한 파일명/형식을 사용합니다.
    /// </summary>
    private static string LoadProjectContext(string workFolder)
    {
        if (string.IsNullOrEmpty(workFolder)) return "";

        // Phase 30-C: HierarchicalMemoryService — 4-layer 계층 메모리 통합 조회
        try
        {
            var hierMemory = new AxCopilot.Services.Agent.HierarchicalMemoryService();
            var merged = hierMemory.BuildMergedContext(workFolder, 8000);
            if (!string.IsNullOrWhiteSpace(merged))
            {
                // @include 지시어 해석
                if (merged.Contains("@"))
                {
                    try
                    {
                        var resolver = new AxCopilot.Services.Agent.AxMdIncludeResolver();
                        merged = resolver.ResolveAsync(merged, workFolder).GetAwaiter().GetResult();
                    }
                    catch (Exception) { /* @include 실패 시 원본 유지 */ }
                }
                return $"\n## Project Context (Hierarchical Memory)\n{merged}\n";
            }
        }
        catch (Exception) { /* 계층 메모리 실패 시 레거시 폴백 */ }

        // 레거시 폴백: 단일 AX.md 탐색 (작업 폴더 → 상위 폴더 순)
        var searchDir = workFolder;
        for (int i = 0; i < 3; i++)
        {
            if (string.IsNullOrEmpty(searchDir)) break;
            var filePath = System.IO.Path.Combine(searchDir, "AX.md");
            if (System.IO.File.Exists(filePath))
            {
                try
                {
                    var content = System.IO.File.ReadAllText(filePath);
                    if (content.Contains("@"))
                    {
                        try
                        {
                            var resolver = new AxCopilot.Services.Agent.AxMdIncludeResolver();
                            var baseDir = System.IO.Path.GetDirectoryName(filePath) ?? workFolder;
                            content = resolver.ResolveAsync(content, baseDir).GetAwaiter().GetResult();
                        }
                        catch (Exception) { /* 비핵심 작업 실패 — UI 차단 방지 */ }
                    }
                    if (content.Length > 8000) content = content[..8000] + "\n... (8000자 초과 생략)";
                    return $"\n## Project Context (from AX.md)\n{content}\n";
                }
                catch (Exception) { /* 비핵심 작업 실패 — UI 차단 방지 */ }
            }
            searchDir = System.IO.Directory.GetParent(searchDir)?.FullName;
        }
        return "";
    }

    /// <summary>Phase 27-B: 현재 파일 경로에 매칭되는 스킬을 시스템 프롬프트에 자동 주입.</summary>
    private string BuildPathBasedSkillSection()
    {
        try
        {
            // 현재 대화에서 언급된 파일 경로 추출 (최근 메시지에서)
            var recentFiles = GetRecentlyMentionedFiles();
            if (recentFiles.Count == 0) return "";

            var allSkills = AxCopilot.Services.Agent.SkillService.Skills;
            var activator = new AxCopilot.Services.Agent.PathBasedSkillActivator();

            var matchedSkills = new List<AxCopilot.Services.Agent.SkillDefinition>();
            foreach (var file in recentFiles)
            {
                var matches = activator.GetActiveSkillsForFile(allSkills, file);
                foreach (var m in matches)
                {
                    if (!matchedSkills.Any(s => s.Name == m.Name))
                        matchedSkills.Add(m);
                }
            }

            return activator.BuildSkillContextInjection(matchedSkills);
        }
        catch (Exception) { return ""; }
    }

    /// <summary>최근 대화에서 파일 경로를 추출합니다.</summary>
    private List<string> GetRecentlyMentionedFiles()
    {
        var files = new List<string>();
        if (_currentConversation?.Messages == null) return files;

        // 최근 5개 메시지에서 파일 경로 패턴 탐색
        var recent = _currentConversation.Messages.TakeLast(5);
        foreach (var msg in recent)
        {
            if (string.IsNullOrEmpty(msg.Content)) continue;
            // 간단한 파일 경로 패턴: 확장자가 있는 경로
            var pathMatches = System.Text.RegularExpressions.Regex.Matches(
                msg.Content, @"[\w./\\-]+\.\w{1,10}");
            foreach (System.Text.RegularExpressions.Match m in pathMatches)
            {
                var path = m.Value;
                if (path.Contains('.') && !path.StartsWith("http"))
                    files.Add(path);
            }
        }
        return files.Distinct().Take(10).ToList();
    }

    // ─── 무지개 글로우 애니메이션 ─────────────────────────────────────────

    private DispatcherTimer? _rainbowTimer;
    private DateTime _rainbowStartTime;

    /// <summary>입력창 테두리에 무지개 그라데이션 회전 애니메이션을 재생합니다 (3초).</summary>
    private void PlayRainbowGlow()
    {
        if (!Llm.EnableChatRainbowGlow) return;

        _rainbowTimer?.Stop();
        _rainbowStartTime = DateTime.UtcNow;

        // 페이드인 (빠르게)
        InputGlowBorder.BeginAnimation(UIElement.OpacityProperty,
            new System.Windows.Media.Animation.DoubleAnimation(0, 0.9, TimeSpan.FromMilliseconds(150)));

        // 그라데이션 회전 타이머 (~60fps) — 스트리밍 종료까지 지속
        _rainbowTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(16) };
        _rainbowTimer.Tick += (_, _) =>
        {
            var elapsed = (DateTime.UtcNow - _rainbowStartTime).TotalMilliseconds;

            // 그라데이션 오프셋 회전
            var shift = (elapsed / 1500.0) % 1.0; // 1.5초에 1바퀴 (느리게)
            var brush = InputGlowBorder.BorderBrush as LinearGradientBrush;
            if (brush == null) return;

            // 시작/끝점 회전 (원형 이동)
            var angle = shift * Math.PI * 2;
            brush.StartPoint = new Point(0.5 + 0.5 * Math.Cos(angle), 0.5 + 0.5 * Math.Sin(angle));
            brush.EndPoint = new Point(0.5 - 0.5 * Math.Cos(angle), 0.5 - 0.5 * Math.Sin(angle));
        };
        _rainbowTimer.Start();
    }

    /// <summary>레인보우 글로우 효과를 페이드아웃하며 중지합니다.</summary>
    private void StopRainbowGlow()
    {
        _rainbowTimer?.Stop();
        _rainbowTimer = null;
        if (InputGlowBorder.Opacity > 0)
        {
            var fadeOut = new System.Windows.Media.Animation.DoubleAnimation(
                InputGlowBorder.Opacity, 0, TimeSpan.FromMilliseconds(600));
            fadeOut.Completed += (_, _) => InputGlowBorder.Opacity = 0;
            InputGlowBorder.BeginAnimation(UIElement.OpacityProperty, fadeOut);
        }
    }

    // ─── 토스트 알림 ──────────────────────────────────────────────────────

    private DispatcherTimer? _toastHideTimer;

    private void ShowToast(string message, string icon = "\uE73E", int durationMs = 2000)
    {
        _toastHideTimer?.Stop();

        ToastText.Text = message;
        ToastIcon.Text = icon;
        ToastBorder.Visibility = Visibility.Visible;

        // 페이드인
        ToastBorder.BeginAnimation(UIElement.OpacityProperty,
            new System.Windows.Media.Animation.DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(200)));

        // 자동 숨기기
        _toastHideTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(durationMs) };
        _toastHideTimer.Tick += (_, _) =>
        {
            _toastHideTimer.Stop();
            var fadeOut = new System.Windows.Media.Animation.DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(300));
            fadeOut.Completed += (_, _) => ToastBorder.Visibility = Visibility.Collapsed;
            ToastBorder.BeginAnimation(UIElement.OpacityProperty, fadeOut);
        };
        _toastHideTimer.Start();
    }

    // ─── 하단 상태바 ──────────────────────────────────────────────────────

    private System.Windows.Media.Animation.Storyboard? _statusSpinStoryboard;

    private void UpdateStatusBar(AgentEvent evt)
    {
        var toolLabel = evt.ToolName switch
        {
            "file_read" or "document_read" => "파일 읽기",
            "file_write" => "파일 쓰기",
            "file_edit" => "파일 수정",
            "html_create" => "HTML 생성",
            "xlsx_create" => "Excel 생성",
            "docx_create" => "Word 생성",
            "csv_create" => "CSV 생성",
            "md_create" => "Markdown 생성",
            "folder_map" => "폴더 탐색",
            "glob" => "파일 검색",
            "grep" => "내용 검색",
            "process" => "명령 실행",
            _ => evt.ToolName,
        };

        switch (evt.Type)
        {
            case AgentEventType.Thinking:
                SetStatus("생각 중...", spinning: true);
                break;
            case AgentEventType.Planning:
                SetStatus($"계획 수립 중 — {evt.StepTotal}단계", spinning: true);
                break;
            case AgentEventType.ToolCall:
                SetStatus($"{toolLabel} 실행 중...", spinning: true);
                break;
            case AgentEventType.ToolResult:
                SetStatus(evt.Success ? $"{toolLabel} 완료" : $"{toolLabel} 실패", spinning: false);
                break;
            case AgentEventType.StepStart:
                SetStatus($"[{evt.StepCurrent}/{evt.StepTotal}] {TruncateForStatus(evt.Summary)}", spinning: true);
                break;
            case AgentEventType.StepDone:
                SetStatus($"[{evt.StepCurrent}/{evt.StepTotal}] 단계 완료", spinning: true);
                break;
            case AgentEventType.SkillCall:
                SetStatus($"스킬 실행 중: {TruncateForStatus(evt.Summary)}", spinning: true);
                break;
            case AgentEventType.Complete:
                SetStatus("작업 완료", spinning: false);
                StopStatusAnimation();
                break;
            case AgentEventType.Error:
                SetStatus("오류 발생", spinning: false);
                StopStatusAnimation();
                break;
            case AgentEventType.Paused:
                SetStatus("⏸ 일시정지", spinning: false);
                break;
            case AgentEventType.Resumed:
                SetStatus("▶ 재개됨", spinning: true);
                break;
        }
    }

    private void SetStatus(string text, bool spinning)
    {
        if (StatusLabel != null) StatusLabel.Text = text;
        if (spinning) StartStatusAnimation();
    }

    private void StartStatusAnimation()
    {
        if (_statusSpinStoryboard != null) return;

        var anim = new System.Windows.Media.Animation.DoubleAnimation
        {
            From = 0, To = 360,
            Duration = TimeSpan.FromSeconds(2),
            RepeatBehavior = System.Windows.Media.Animation.RepeatBehavior.Forever,
        };

        _statusSpinStoryboard = new System.Windows.Media.Animation.Storyboard();
        System.Windows.Media.Animation.Storyboard.SetTarget(anim, StatusDiamond);
        System.Windows.Media.Animation.Storyboard.SetTargetProperty(anim,
            new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
        _statusSpinStoryboard.Children.Add(anim);
        _statusSpinStoryboard.Begin();
    }

    private void StopStatusAnimation()
    {
        _statusSpinStoryboard?.Stop();
        _statusSpinStoryboard = null;
    }

    private void SetStatusIdle()
    {
        StopStatusAnimation();
        if (StatusLabel != null) StatusLabel.Text = "대기 중";
        if (StatusElapsed != null) StatusElapsed.Text = "";
        if (StatusTokens != null) StatusTokens.Text = "";
    }

    private void UpdateStatusTokens(int inputTokens, int outputTokens)
    {
        if (StatusTokens == null) return;
        var llm = Llm;
        var (inCost, outCost) = Services.TokenEstimator.EstimateCost(
            inputTokens, outputTokens, llm.Service, llm.Model);
        var totalCost = inCost + outCost;
        var costText = totalCost > 0 ? $" · {Services.TokenEstimator.FormatCost(totalCost)}" : "";
        StatusTokens.Text = $"↑{Services.TokenEstimator.Format(inputTokens)} ↓{Services.TokenEstimator.Format(outputTokens)}{costText}";
    }

    private static string TruncateForStatus(string? text, int max = 40)
    {
        if (string.IsNullOrEmpty(text)) return "";
        return text.Length <= max ? text : text[..max] + "…";
    }

    // ─── 헬퍼 ─────────────────────────────────────────────────────────────
    private static System.Windows.Media.SolidColorBrush BrushFromHex(string hex)
    {
        var c = ThemeResourceHelper.HexColor(hex);
        return new System.Windows.Media.SolidColorBrush(c);
    }
}
