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

namespace AxCopilot.Services;

public class SettingsService
{
    private static readonly string AppDataDir = InitAppDataDir();

    /// <summary>AppData 디렉터리 초기화. 기존 AxCommander 폴더가 있으면 AxCopilot으로 마이그레이션.</summary>
    private static string InitAppDataDir()
    {
        var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        var newDir = Path.Combine(appData, "AxCopilot");
        var oldDir = Path.Combine(appData, "AxCommander");
        if (!Directory.Exists(newDir) && Directory.Exists(oldDir))
            try { Directory.Move(oldDir, newDir); } catch (Exception) { }
        return newDir;
    }

    public static readonly string SettingsPath = Path.Combine(AppDataDir, "settings.dat");
    private static readonly string LegacyJsonPath = Path.Combine(AppDataDir, "settings.json");

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        WriteIndented = true,
        PropertyNameCaseInsensitive = true
    };

    private AppSettings _settings = new();
    public AppSettings Settings => _settings;

    public event EventHandler? SettingsChanged;

    /// <summary>마이그레이션이 수행된 경우 변경 내역 문자열. null이면 마이그레이션 없음.</summary>
    public string? MigrationSummary { get; private set; }

    /// <summary>
    /// 설정 파일의 스키마 버전 (앱 버전과 별개).
    /// ★ 버전 관리 규칙:
    ///   - 앱 릴리스 버전은 AxCopilot.csproj → &lt;Version&gt; 에서만 관리합니다.
    ///   - 이 값은 settings.json 구조(필드 추가·이름 변경 등)가 바뀔 때만 올립니다.
    ///   - 올릴 때마다 MigrateIfNeeded() 에 해당 버전으로의 마이그레이션 블록을 추가합니다.
    ///   - 현재 매핑: 앱 v1.0.0~1.0.2 → 설정 v1.0 / 앱 v1.0.3~ → 설정 v1.1
    /// </summary>
    private const string CurrentSettingsVersion = "1.2";

    public void Load()
    {
        EnsureDirectories();

        // 1. 암호화된 설정 파일 우선
        if (File.Exists(SettingsPath))
        {
            try
            {
                var encrypted = File.ReadAllText(SettingsPath);
                var json = CryptoService.PortableDecrypt(encrypted);
                if (string.IsNullOrEmpty(json))
                    throw new InvalidOperationException("복호화 결과가 비어 있습니다.");
                _settings = JsonSerializer.Deserialize<AppSettings>(json, JsonOptions) ?? new();
                MigrateIfNeeded();
                return;
            }
            catch (Exception ex)
            {
                var backupPath = SettingsPath + ".bak";
                try { File.Copy(SettingsPath, backupPath, overwrite: true); }
                catch (Exception backupEx) { LogService.Warn($"settings.dat 백업 실패: {backupEx.Message}"); }
                LogService.Error($"settings.dat 복호화/파싱 실패, 기본값으로 복구: {ex.Message}");
            }
        }

        // 2. 레거시 평문 JSON 마이그레이션
        if (File.Exists(LegacyJsonPath))
        {
            try
            {
                var json = File.ReadAllText(LegacyJsonPath);
                _settings = JsonSerializer.Deserialize<AppSettings>(json, JsonOptions) ?? new();
                MigrateIfNeeded();
                Save(); // 암호화된 settings.dat로 저장
                try { File.Delete(LegacyJsonPath); }
                catch (Exception delEx) { LogService.Warn($"레거시 settings.json 삭제 실패: {delEx.Message}"); }
                LogService.Info("설정 마이그레이션: settings.json → settings.dat (암호화 적용)");
                return;
            }
            catch (Exception ex)
            {
                LogService.Error($"레거시 settings.json 파싱 실패: {ex.Message}");
            }
        }

        // 3. 신규 설치
        _settings = CreateDefaults();
        _settings.Version = CurrentSettingsVersion;
        Save();
    }

    /// <summary>
    /// 설정 파일 버전을 확인하고, 이전 버전이면 순차적으로 마이그레이션합니다.
    /// 새 프로퍼티는 C# 기본값으로 자동 적용되며,
    /// 구조 변경(키 이름 변경, 하위 객체 이동 등)만 여기서 처리합니다.
    /// </summary>
    private void MigrateIfNeeded()
    {
        var ver = _settings.Version ?? "1.0";
        var migrated = false;

        // ── 1.0 → 1.1 마이그레이션 ──────────────────────────────────────────
        if (IsVersionLessThan(ver, "1.1"))
        {
            // 폴더 별칭 프리픽스 변경: ~xxx → cd xxx (v1.1)
            foreach (var alias in _settings.Aliases.Where(a => a.Type == "folder"))
            {
                if (alias.Key.StartsWith("~"))
                    alias.Key = "cd " + alias.Key.TrimStart('~');
            }
            // 워크스페이스 기호 변경 알림은 도움말에서 안내
            LogService.Info("설정 마이그레이션: 1.0 → 1.1 (폴더 별칭 prefix ~ → cd)");
            ver = "1.1";
            migrated = true;
        }

        // ── 1.1 → 1.2 마이그레이션 ──────────────────────────────────────────
        if (IsVersionLessThan(ver, "1.2"))
        {
            // 파일 대화상자 연동 기능 기본값 비활성화 (v1.6.2)
            // 브라우저 파일 업로드·메일 첨부 시 런처가 의도치 않게 열리는 오작동 방지
            _settings.Launcher.EnableFileDialogIntegration = false;
            LogService.Info("설정 마이그레이션: 1.1 → 1.2 (파일 대화상자 연동 비활성화)");
            ver = "1.2";
            migrated = true;
        }

        // ── 향후 마이그레이션은 여기에 추가 ─────────────────────────────────
        // if (IsVersionLessThan(ver, "1.3")) { ... ver = "1.3"; migrated = true; }

        if (migrated)
        {
            _settings.Version = CurrentSettingsVersion;
            Save();
            MigrationSummary = $"설정이 v{CurrentSettingsVersion}으로 업데이트되었습니다.\n\n"
                             + "• 파일 대화상자 연동 기능이 비활성화되었습니다.\n"
                             + "  웹 브라우저 파일 업로드 시 런처가 열리는 문제를 방지합니다.\n"
                             + "  필요 시 설정 → 기능 → AI 기능에서 다시 활성화할 수 있습니다.";
            LogService.Info($"설정 파일 마이그레이션 완료 → v{CurrentSettingsVersion}");
        }
    }

    private static bool IsVersionLessThan(string current, string target)
    {
        if (Version.TryParse(current, out var cv) && Version.TryParse(target, out var tv))
            return cv < tv;
        return string.Compare(current, target, StringComparison.Ordinal) < 0;
    }

    public void Save()
    {
        EnsureDirectories();
        var json = JsonSerializer.Serialize(_settings, JsonOptions);
        var encrypted = CryptoService.PortableEncrypt(json);
        File.WriteAllText(SettingsPath, encrypted);
        SettingsChanged?.Invoke(this, EventArgs.Empty);
    }

    private static void EnsureDirectories()
    {
        Directory.CreateDirectory(AppDataDir);
        Directory.CreateDirectory(Path.Combine(AppDataDir, "logs"));
        Directory.CreateDirectory(Path.Combine(AppDataDir, "crashes"));
        Directory.CreateDirectory(Path.Combine(AppDataDir, "skills"));
    }

    private static AppSettings CreateDefaults()
    {
        var indexPaths = new List<string>
        {
            "%USERPROFILE%\\Desktop",
            "%APPDATA%\\Microsoft\\Windows\\Start Menu"
        };

        // D:\Non_Documents 폴더가 존재하면 기본 인덱스 경로에 추가
        const string nonDocPath = "D:\\Non_Documents";
        if (Directory.Exists(nonDocPath))
            indexPaths.Add(nonDocPath);

        return new AppSettings
        {
            IndexPaths = indexPaths,
            Aliases = new List<AliasEntry>
            {
                new() { Key = "@blog", Type = "url", Target = "https://example.com", Description = "내 블로그" },
                new() { Key = "~home", Type = "folder", Target = "%USERPROFILE%", Description = "홈 폴더" },
            },
            // 커스텀 테마 초기값 (theme: "custom"으로 변경 시 활성화)
            Launcher = new()
            {
                CustomTheme = new AxCopilot.Models.CustomThemeColors()
            }
        };
    }
}
