using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Input;
using AxCopilot.Services;

namespace AxCopilot.Views;

/// <summary>
/// L5-5: 배치 파일 이름변경 창.
/// 여러 파일을 변수 패턴 또는 정규식으로 미리보기 후 일괄 적용합니다.
/// </summary>
public partial class BatchRenameWindow : Window
{
    private readonly ObservableCollection<RenameEntry> _entries = new();
    private bool _keepExt  = true;
    private bool _regexMode = false;
    private int  _startNumber = 1;

    // ──────────────────────────────────────────────────────────────────────
    public BatchRenameWindow()
    {
        InitializeComponent();
        PreviewGrid.ItemsSource = _entries;

        // 항목 변경 → 카운트 배지 + 빈 상태 갱신
        _entries.CollectionChanged += (_, _) => RefreshUI();

        // 드래그 앤 드롭
        Drop     += Window_Drop;
        DragOver += Window_DragOver;
        DragLeave += (_, _) => DropHintOverlay.Visibility = Visibility.Collapsed;
    }

    // ─── 외부에서 파일 목록 주입 ─────────────────────────────────────────
    public void AddFiles(IEnumerable<string> paths)
    {
        foreach (var p in paths)
        {
            if (!File.Exists(p)) continue;
            if (_entries.Any(e => string.Equals(e.OriginalPath, p, StringComparison.OrdinalIgnoreCase))) continue;
            _entries.Add(new RenameEntry
            {
                OriginalPath = p,
                OriginalName = Path.GetFileName(p)
            });
        }
        UpdatePreviews();
    }

    // ─── 패턴 엔진 ───────────────────────────────────────────────────────
    private static string ApplyPattern(
        string pattern,
        string originalName,
        string ext,
        int index,
        int startNum,
        bool keepExt,
        bool regexMode)
    {
        if (regexMode)
        {
            // /regex/replacement/ 형식
            var m = Regex.Match(pattern, @"^/(.+)/([^/]*)/?$");
            if (m.Success)
            {
                var rxPat  = m.Groups[1].Value;
                var repl   = m.Groups[2].Value;
                try
                {
                    var result = Regex.Replace(originalName, rxPat, repl);
                    if (keepExt && !string.IsNullOrEmpty(ext) &&
                        !result.EndsWith(ext, StringComparison.OrdinalIgnoreCase))
                        result += ext;
                    return result;
                }
                catch { return "⚠ 정규식 오류"; }
            }
            // 패턴 불완전 → 원본 그대로
            return originalName + ext;
        }

        // 변수 모드
        var n = index + startNum;
        var newName = pattern;

        // {n:자릿수} — 자릿수 패딩
        newName = Regex.Replace(newName, @"\{n:(\d+)\}", rm =>
        {
            if (int.TryParse(rm.Groups[1].Value, out var digits))
                return n.ToString($"D{digits}");
            return n.ToString();
        });

        // {date:format}
        newName = Regex.Replace(newName, @"\{date:([^}]+)\}", rm =>
        {
            try   { return DateTime.Today.ToString(rm.Groups[1].Value); }
            catch { return DateTime.Today.ToString("yyyy-MM-dd"); }
        });

        newName = newName
            .Replace("{n}",    n.ToString())
            .Replace("{name}", originalName)
            .Replace("{orig}", originalName)
            .Replace("{date}", DateTime.Today.ToString("yyyy-MM-dd"))
            .Replace("{ext}",  ext.TrimStart('.'));

        // 확장자 유지
        if (keepExt && !string.IsNullOrEmpty(ext))
        {
            if (!Path.HasExtension(newName))
                newName += ext;
        }

        return newName;
    }

    private void UpdatePreviews()
    {
        var pattern    = PatternBox?.Text ?? "{n}_{name}";
        var startNum   = _startNumber;
        var keepExt    = _keepExt;
        var regexMode  = _regexMode;

        // 새 이름 계산
        for (int i = 0; i < _entries.Count; i++)
        {
            var entry   = _entries[i];
            var ext     = Path.GetExtension(entry.OriginalPath);
            var nameNoExt = Path.GetFileNameWithoutExtension(entry.OriginalPath);
            entry.NewName = ApplyPattern(pattern, nameNoExt, ext, i, startNum, keepExt, regexMode);
        }

        // 충돌 감지 (같은 폴더 내 같은 새 이름 OR 기존 파일)
        var grouped = _entries
            .GroupBy(e => Path.Combine(
                Path.GetDirectoryName(e.OriginalPath) ?? "",
                e.NewName),
                StringComparer.OrdinalIgnoreCase);

        var conflictPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        foreach (var g in grouped)
        {
            if (g.Count() > 1)
                foreach (var e in g) conflictPaths.Add(e.OriginalPath);
        }

        foreach (var entry in _entries)
        {
            var destPath = Path.Combine(
                Path.GetDirectoryName(entry.OriginalPath) ?? "",
                entry.NewName);

            // 충돌: 동명 중복 OR 목적지 파일이 이미 존재(원본 제외)
            var nameConflict = conflictPaths.Contains(entry.OriginalPath);
            var destExists   = File.Exists(destPath) &&
                               !string.Equals(destPath, entry.OriginalPath, StringComparison.OrdinalIgnoreCase);
            entry.HasConflict = nameConflict || destExists;
        }

        RefreshUI();
    }

    private void RefreshUI()
    {
        var count    = _entries.Count;
        var conflicts = _entries.Count(e => e.HasConflict);

        FileCountBadge.Text = count > 0 ? $"— {count}개 파일" : "";
        EmptyState.Visibility  = count == 0 ? Visibility.Visible  : Visibility.Collapsed;
        PreviewGrid.Visibility = count == 0 ? Visibility.Collapsed : Visibility.Visible;

        ConflictLabel.Text = conflicts > 0 ? $"⚠ 충돌 {conflicts}개" : "";
    }

    // ─── 타이틀바 ────────────────────────────────────────────────────────
    private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed) DragMove();
    }

    private void BtnClose_Click(object sender, MouseButtonEventArgs e) => Close();

    // ─── 패턴 입력 ───────────────────────────────────────────────────────
    private void PatternBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        => UpdatePreviews();

    private void StartNumberBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
    {
        if (int.TryParse(StartNumberBox.Text, out var n) && n >= 0)
            _startNumber = n;
        UpdatePreviews();
    }

    // ─── 모드 선택 ───────────────────────────────────────────────────────
    private void BtnModeVar_Click(object sender, MouseButtonEventArgs e)
    {
        _regexMode = false;
        SetModeUI();
        UpdatePreviews();
    }

    private void BtnModeRegex_Click(object sender, MouseButtonEventArgs e)
    {
        _regexMode = true;
        SetModeUI();
        if (string.IsNullOrWhiteSpace(PatternBox.Text) || !PatternBox.Text.StartsWith('/'))
            PatternBox.Text = "/(old)/(new)/";
        UpdatePreviews();
    }

    private void SetModeUI()
    {
        var accent  = TryFindResource("AccentColor") as System.Windows.Media.Brush
                      ?? System.Windows.Media.Brushes.DodgerBlue;
        var sec     = TryFindResource("SecondaryText") as System.Windows.Media.Brush
                      ?? System.Windows.Media.Brushes.Gray;
        var dimBg   = new System.Windows.Media.SolidColorBrush(
                          System.Windows.Media.Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));

        if (_regexMode)
        {
            BtnModeVar.Background       = dimBg;
            BtnModeVarText.Foreground   = sec;
            BtnModeRegex.Background     = accent;
            BtnModeRegexText.Foreground = System.Windows.Media.Brushes.White;
        }
        else
        {
            BtnModeVar.Background       = accent;
            BtnModeVarText.Foreground   = System.Windows.Media.Brushes.White;
            BtnModeRegex.Background     = dimBg;
            BtnModeRegexText.Foreground = sec;
        }
    }

    // ─── 확장자 유지 토글 ────────────────────────────────────────────────
    private void ExtToggle_Click(object sender, MouseButtonEventArgs e)
    {
        _keepExt = !_keepExt;

        var accent = TryFindResource("AccentColor") as System.Windows.Media.Brush
                     ?? System.Windows.Media.Brushes.DodgerBlue;
        var gray   = new System.Windows.Media.SolidColorBrush(
                         System.Windows.Media.Color.FromArgb(0x40, 0xFF, 0xFF, 0xFF));

        ExtToggle.Background       = _keepExt ? accent : gray;
        ExtThumb.HorizontalAlignment = _keepExt ? HorizontalAlignment.Right : HorizontalAlignment.Left;
        ExtThumb.Margin              = _keepExt ? new Thickness(0, 0, 1, 0) : new Thickness(1, 0, 0, 0);

        UpdatePreviews();
    }

    // ─── 힌트 팝업 ───────────────────────────────────────────────────────
    private void BtnHint_Click(object sender, MouseButtonEventArgs e)
        => HintPopup.IsOpen = !HintPopup.IsOpen;

    // ─── 파일/폴더 추가 ──────────────────────────────────────────────────
    private void BtnAddFolder_Click(object sender, MouseButtonEventArgs e)
    {
        using var dlg = new System.Windows.Forms.FolderBrowserDialog
        {
            Description  = "파일이 있는 폴더를 선택하세요",
            ShowNewFolderButton = false
        };
        if (dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;

        var files = Directory.GetFiles(dlg.SelectedPath);
        Array.Sort(files);
        AddFiles(files);
    }

    private void BtnAddFiles_Click(object sender, MouseButtonEventArgs e)
    {
        using var dlg = new System.Windows.Forms.OpenFileDialog
        {
            Title     = "이름변경할 파일 선택",
            Multiselect = true,
            Filter    = "모든 파일 (*.*)|*.*"
        };
        if (dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;

        var files = dlg.FileNames.OrderBy(f => f).ToArray();
        AddFiles(files);
    }

    private void BtnRemoveSelected_Click(object sender, MouseButtonEventArgs e)
    {
        var selected = PreviewGrid.SelectedItems.OfType<RenameEntry>().ToList();
        foreach (var item in selected) _entries.Remove(item);
        UpdatePreviews();
    }

    private void BtnClearAll_Click(object sender, MouseButtonEventArgs e)
    {
        _entries.Clear();
        RefreshUI();
    }

    // ─── 적용 ────────────────────────────────────────────────────────────
    private void BtnApply_Click(object sender, MouseButtonEventArgs e)
    {
        var toRename = _entries
            .Where(en => en.NewName != en.OriginalName && !en.HasConflict)
            .ToList();

        if (toRename.Count == 0)
        {
            NotificationService.Notify("배치 이름변경", "변경할 파일이 없습니다. 패턴을 확인하거나 충돌을 해소하세요.");
            return;
        }

        int ok = 0, fail = 0;
        foreach (var entry in toRename)
        {
            try
            {
                var destPath = Path.Combine(
                    Path.GetDirectoryName(entry.OriginalPath) ?? "",
                    entry.NewName);

                File.Move(entry.OriginalPath, destPath);

                // 성공 후 엔트리 갱신 (새 경로로 업데이트)
                entry.OriginalPath = destPath;
                entry.OriginalName = entry.NewName;
                ok++;
            }
            catch (Exception ex)
            {
                LogService.Warn($"배치 이름변경 실패: {entry.OriginalPath} → {entry.NewName} — {ex.Message}");
                fail++;
            }
        }

        // 미리보기 갱신 (적용 후 남은 항목)
        UpdatePreviews();

        var msg = fail > 0
            ? $"{ok}개 이름변경 완료, {fail}개 실패"
            : $"{ok}개 파일 이름변경 완료";
        NotificationService.Notify("AX Copilot", msg);
        LogService.Info($"배치 이름변경: {msg}");
    }

    // ─── 드래그 앤 드롭 ──────────────────────────────────────────────────
    private void Window_DragOver(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            e.Effects = DragDropEffects.Copy;
            DropHintOverlay.Visibility = Visibility.Visible;
        }
        else
        {
            e.Effects = DragDropEffects.None;
        }
        e.Handled = true;
    }

    private void Window_Drop(object sender, DragEventArgs e)
    {
        DropHintOverlay.Visibility = Visibility.Collapsed;

        if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return;

        var dropped = (string[])e.Data.GetData(DataFormats.FileDrop);
        var files   = new List<string>();

        foreach (var p in dropped)
        {
            if (File.Exists(p))
            {
                files.Add(p);
            }
            else if (Directory.Exists(p))
            {
                files.AddRange(Directory.GetFiles(p).OrderBy(f => f));
            }
        }

        AddFiles(files);
    }
}

// ─── RenameEntry 모델 ────────────────────────────────────────────────────────
public class RenameEntry : INotifyPropertyChanged
{
    public string OriginalPath { get; set; } = "";

    private string _originalName = "";
    public string OriginalName
    {
        get => _originalName;
        set { _originalName = value; OnPropertyChanged(); OnPropertyChanged(nameof(StatusText)); }
    }

    private string _newName = "";
    public string NewName
    {
        get => _newName;
        set { _newName = value; OnPropertyChanged(); OnPropertyChanged(nameof(StatusText)); }
    }

    private bool _hasConflict;
    public bool HasConflict
    {
        get => _hasConflict;
        set { _hasConflict = value; OnPropertyChanged(); OnPropertyChanged(nameof(StatusText)); }
    }

    public string StatusText =>
        HasConflict
            ? "⚠ 충돌"
            : OriginalName == NewName
                ? "─"
                : "✓";

    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? name = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
