using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using AxCopilot.Models;

namespace AxCopilot.Services.Agent;

/// <summary>
/// Phase 28: 세션 매니저.
/// CC의 세션 관리와 동등: 대화 상태를 저장/복원/포크/태그/검색합니다.
/// 세션은 %APPDATA%\AxCopilot\sessions\ 에 JSON으로 저장됩니다.
/// </summary>
public class SessionManager
{
    private static readonly string SessionDir = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "AxCopilot", "sessions");

    private static readonly JsonSerializerOptions _json = new()
    {
        WriteIndented = true,
        PropertyNameCaseInsensitive = true,
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    };

    /// <summary>현재 세션을 디스크에 저장합니다.</summary>
    public async Task SaveSessionAsync(AgentSession session, CancellationToken ct = default)
    {
        EnsureDir();
        var filePath = GetSessionPath(session.Id);
        session.UpdatedAt = DateTime.UtcNow;

        var json = JsonSerializer.Serialize(session, _json);
        await File.WriteAllTextAsync(filePath, json, Encoding.UTF8, ct);
    }

    /// <summary>세션을 ID로 복원합니다.</summary>
    public async Task<AgentSession?> LoadSessionAsync(string sessionId, CancellationToken ct = default)
    {
        var filePath = GetSessionPath(sessionId);
        if (!File.Exists(filePath)) return null;

        var json = await File.ReadAllTextAsync(filePath, Encoding.UTF8, ct);
        return JsonSerializer.Deserialize<AgentSession>(json, _json);
    }

    /// <summary>저장된 세션 목록을 반환합니다 (최신순).</summary>
    public List<AgentSessionSummary> ListSessions(int limit = 50)
    {
        EnsureDir();
        var summaries = new List<AgentSessionSummary>();

        foreach (var file in Directory.GetFiles(SessionDir, "*.session.json")
            .OrderByDescending(f => File.GetLastWriteTimeUtc(f))
            .Take(limit))
        {
            try
            {
                var json = File.ReadAllText(file, Encoding.UTF8);
                var session = JsonSerializer.Deserialize<AgentSession>(json, _json);
                if (session == null) continue;

                summaries.Add(new AgentSessionSummary
                {
                    Id = session.Id,
                    Name = session.Name,
                    Tab = session.Tab,
                    Tags = session.Tags,
                    MessageCount = session.Messages?.Count ?? 0,
                    CreatedAt = session.CreatedAt,
                    UpdatedAt = session.UpdatedAt,
                    WorkFolder = session.WorkFolder,
                });
            }
            catch (Exception) { }
        }

        return summaries;
    }

    /// <summary>세션을 포크합니다 (현재 상태에서 분기).</summary>
    public async Task<AgentSession> ForkSessionAsync(string sourceId, string? newName = null, CancellationToken ct = default)
    {
        var source = await LoadSessionAsync(sourceId, ct);
        if (source == null) throw new InvalidOperationException($"세션을 찾을 수 없습니다: {sourceId}");

        var fork = new AgentSession
        {
            Id = GenerateSessionId(),
            Name = newName ?? $"{source.Name} (fork)",
            Tab = source.Tab,
            WorkFolder = source.WorkFolder,
            Model = source.Model,
            Messages = source.Messages?.ToList() ?? new(),
            Tags = new List<string>(source.Tags ?? new()) { "forked" },
            ParentSessionId = sourceId,
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow,
        };

        await SaveSessionAsync(fork, ct);
        return fork;
    }

    /// <summary>세션에 태그를 추가합니다.</summary>
    public async Task TagSessionAsync(string sessionId, string tag, CancellationToken ct = default)
    {
        var session = await LoadSessionAsync(sessionId, ct);
        if (session == null) return;

        session.Tags ??= new();
        if (!session.Tags.Contains(tag, StringComparer.OrdinalIgnoreCase))
        {
            session.Tags.Add(tag);
            await SaveSessionAsync(session, ct);
        }
    }

    /// <summary>세션 이름을 변경합니다.</summary>
    public async Task RenameSessionAsync(string sessionId, string newName, CancellationToken ct = default)
    {
        var session = await LoadSessionAsync(sessionId, ct);
        if (session == null) return;

        session.Name = newName;
        await SaveSessionAsync(session, ct);
    }

    /// <summary>세션을 삭제합니다.</summary>
    public void DeleteSession(string sessionId)
    {
        var filePath = GetSessionPath(sessionId);
        if (File.Exists(filePath)) File.Delete(filePath);
    }

    /// <summary>태그로 세션을 검색합니다.</summary>
    public List<AgentSessionSummary> FindByTag(string tag, int limit = 20)
    {
        return ListSessions(200)
            .Where(s => s.Tags?.Contains(tag, StringComparer.OrdinalIgnoreCase) ?? false)
            .Take(limit)
            .ToList();
    }

    /// <summary>최근 세션을 반환합니다 (--continue 동등).</summary>
    public AgentSessionSummary? GetMostRecent(string? tab = null)
    {
        var sessions = ListSessions(10);
        if (tab != null)
            sessions = sessions.Where(s => s.Tab == tab).ToList();
        return sessions.FirstOrDefault();
    }

    /// <summary>오래된 세션을 정리합니다.</summary>
    public int CleanupOldSessions(int retentionDays = 30)
    {
        EnsureDir();
        var cutoff = DateTime.UtcNow.AddDays(-retentionDays);
        int deleted = 0;

        foreach (var file in Directory.GetFiles(SessionDir, "*.session.json"))
        {
            try
            {
                if (File.GetLastWriteTimeUtc(file) < cutoff)
                {
                    File.Delete(file);
                    deleted++;
                }
            }
            catch (Exception) { }
        }

        return deleted;
    }

    private static string GetSessionPath(string sessionId)
        => Path.Combine(SessionDir, $"{sessionId}.session.json");

    private static string GenerateSessionId()
        => DateTime.UtcNow.ToString("yyyyMMddHHmmss") + "_" + Guid.NewGuid().ToString("N")[..8];

    private static void EnsureDir()
    {
        if (!Directory.Exists(SessionDir))
            Directory.CreateDirectory(SessionDir);
    }
}

/// <summary>저장 가능한 에이전트 세션.</summary>
public class AgentSession
{
    [JsonPropertyName("id")]
    public string Id { get; set; } = "";

    [JsonPropertyName("name")]
    public string Name { get; set; } = "";

    [JsonPropertyName("tab")]
    public string Tab { get; set; } = "Chat";

    [JsonPropertyName("work_folder")]
    public string WorkFolder { get; set; } = "";

    [JsonPropertyName("model")]
    public string Model { get; set; } = "";

    [JsonPropertyName("messages")]
    public List<ChatMessage>? Messages { get; set; }

    [JsonPropertyName("tags")]
    public List<string>? Tags { get; set; }

    [JsonPropertyName("parent_session_id")]
    public string? ParentSessionId { get; set; }

    [JsonPropertyName("created_at")]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;

    [JsonPropertyName("updated_at")]
    public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

/// <summary>세션 목록 표시용 요약.</summary>
public class AgentSessionSummary
{
    public string Id { get; init; } = "";
    public string Name { get; init; } = "";
    public string Tab { get; init; } = "";
    public List<string>? Tags { get; init; }
    public int MessageCount { get; init; }
    public DateTime CreatedAt { get; init; }
    public DateTime UpdatedAt { get; init; }
    public string WorkFolder { get; init; } = "";
}
