using System.IO;
using System.Text.Json;
using System.Windows.Threading;

namespace AxCopilot.Services;

/// <summary>
/// 이벤트 기반 에이전트 트리거 서비스.
/// 파일 변경, 스케줄, Git 이벤트를 감지하여 에이전트를 자동 실행합니다.
/// </summary>
public class AgentTriggerService : IDisposable
{
    private readonly SettingsService _settings;
    private FileSystemWatcher? _fileWatcher;
    private DispatcherTimer? _scheduleTimer;
    private readonly List<TriggerRule> _rules = new();
    private bool _disposed;

    /// <summary>트리거 발동 시 에이전트 실행 콜백. (triggerName, prompt) → void</summary>
    public Action<string, string>? OnTriggerFired { get; set; }

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

    /// <summary>트리거 규칙을 로드하고 모니터링을 시작합니다.</summary>
    public void Start()
    {
        LoadRules();
        StartFileWatcher();
        StartScheduleTimer();
        LogService.Info($"에이전트 트리거 서비스 시작: {_rules.Count}개 규칙");
    }

    /// <summary>트리거 규칙을 다시 로드합니다.</summary>
    public void Reload()
    {
        Stop();
        Start();
    }

    public void Stop()
    {
        _fileWatcher?.Dispose();
        _fileWatcher = null;
        _scheduleTimer?.Stop();
    }

    private void LoadRules()
    {
        _rules.Clear();
        var triggerFile = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "AxCopilot", "triggers.json");

        if (!File.Exists(triggerFile)) return;

        try
        {
            var json = File.ReadAllText(triggerFile);
            var loaded = JsonSerializer.Deserialize<List<TriggerRule>>(json);
            if (loaded != null) _rules.AddRange(loaded.Where(r => r.Enabled));
        }
        catch (Exception ex)
        {
            LogService.Warn($"트리거 규칙 로드 실패: {ex.Message}");
        }
    }

    private void StartFileWatcher()
    {
        var fileRules = _rules.Where(r => r.Type == "file_change").ToList();
        if (fileRules.Count == 0) return;

        var workFolder = _settings.Settings.Llm.WorkFolder;
        if (string.IsNullOrEmpty(workFolder) || !Directory.Exists(workFolder)) return;

        _fileWatcher = new FileSystemWatcher(workFolder)
        {
            IncludeSubdirectories = true,
            NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName,
            EnableRaisingEvents = true,
        };

        // 디바운스용 — 짧은 시간 내 여러 변경을 하나로 묶음
        DateTime lastTrigger = DateTime.MinValue;

        _fileWatcher.Changed += (_, e) =>
        {
            if ((DateTime.Now - lastTrigger).TotalSeconds < 5) return;

            foreach (var rule in fileRules)
            {
                if (MatchesPattern(e.FullPath, rule.Pattern))
                {
                    lastTrigger = DateTime.Now;
                    var prompt = rule.Prompt.Replace("{file}", e.FullPath).Replace("{name}", e.Name ?? "");
                    System.Windows.Application.Current?.Dispatcher.BeginInvoke(() =>
                        OnTriggerFired?.Invoke(rule.Name, prompt));
                    break;
                }
            }
        };
    }

    private void StartScheduleTimer()
    {
        var scheduleRules = _rules.Where(r => r.Type == "schedule").ToList();
        if (scheduleRules.Count == 0) return;

        _scheduleTimer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(1) };
        _scheduleTimer.Tick += (_, _) =>
        {
            var now = DateTime.Now;
            foreach (var rule in scheduleRules)
            {
                if (ShouldRunSchedule(rule, now))
                {
                    rule.LastRun = now;
                    OnTriggerFired?.Invoke(rule.Name, rule.Prompt);
                }
            }
        };
        _scheduleTimer.Start();
    }

    private static bool MatchesPattern(string filePath, string? pattern)
    {
        if (string.IsNullOrEmpty(pattern)) return true;
        // 간단한 확장자/폴더 패턴 매칭
        if (pattern.StartsWith("*."))
            return filePath.EndsWith(pattern[1..], StringComparison.OrdinalIgnoreCase);
        return filePath.Contains(pattern, StringComparison.OrdinalIgnoreCase);
    }

    private static bool ShouldRunSchedule(TriggerRule rule, DateTime now)
    {
        if (rule.LastRun.HasValue && (now - rule.LastRun.Value).TotalMinutes < (rule.IntervalMinutes ?? 60))
            return false;
        // 시간 범위 체크 (근무 시간만)
        if (rule.ActiveHourStart.HasValue && now.Hour < rule.ActiveHourStart.Value) return false;
        if (rule.ActiveHourEnd.HasValue && now.Hour >= rule.ActiveHourEnd.Value) return false;
        return true;
    }

    /// <summary>수동으로 트리거를 실행합니다.</summary>
    public void FireManual(string ruleName)
    {
        var rule = _rules.FirstOrDefault(r => r.Name == ruleName);
        if (rule != null)
            OnTriggerFired?.Invoke(rule.Name, rule.Prompt);
    }

    /// <summary>현재 로드된 트리거 규칙 목록.</summary>
    public IReadOnlyList<TriggerRule> Rules => _rules;

    public void Dispose()
    {
        if (_disposed) return;
        _disposed = true;
        Stop();
    }
}

/// <summary>트리거 규칙 정의.</summary>
public class TriggerRule
{
    public string Name { get; set; } = "";
    public string Type { get; set; } = "file_change"; // file_change | schedule | git
    public string Prompt { get; set; } = "";
    public string? Pattern { get; set; } // file_change: 확장자/경로 패턴
    public int? IntervalMinutes { get; set; } // schedule: 실행 간격 (분)
    public int? ActiveHourStart { get; set; } // schedule: 활성 시작 시각 (0~23)
    public int? ActiveHourEnd { get; set; } // schedule: 활성 종료 시각
    public bool Enabled { get; set; } = true;
    public DateTime? LastRun { get; set; }
}
