using System.IO;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// .skill.json 파일을 읽어 IActionHandler로 변환하는 로더.
/// JSON 스킬은 외부 API를 호출하는 동적 핸들러를 코드 없이 정의합니다.
/// </summary>
public static class JsonSkillLoader
{
    public static IActionHandler? Load(string filePath)
    {
        var json = File.ReadAllText(filePath);
        var def = JsonSerializer.Deserialize<JsonSkillDefinition>(json,
            new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

        if (def == null) return null;
        return new JsonSkillHandler(def);
    }
}

public class JsonSkillDefinition
{
    public string Id { get; set; } = "";
    public string Name { get; set; } = "";
    public string Version { get; set; } = "1.0";
    public string Prefix { get; set; } = "";
    public JsonSkillCredential? Credential { get; set; }
    public JsonSkillRequest? Request { get; set; }
    public JsonSkillResponse? Response { get; set; }
    public JsonSkillCache? Cache { get; set; }
}

public class JsonSkillCredential
{
    public string Type { get; set; } = "bearer_token"; // bearer_token | basic_auth
    public string CredentialKey { get; set; } = "";
}

public class JsonSkillRequest
{
    public string Method { get; set; } = "GET";
    public string Url { get; set; } = "";
    public Dictionary<string, string>? Headers { get; set; }
    public object? Body { get; set; }
}

public class JsonSkillResponse
{
    public string ResultsPath { get; set; } = "results";
    public string TitleField { get; set; } = "title";
    public string? SubtitleField { get; set; }
    public string? ActionUrl { get; set; }
}

public class JsonSkillCache
{
    public int Ttl { get; set; } = 0; // 초 단위
}

/// <summary>
/// JSON 스킬 정의를 기반으로 실제 HTTP 호출을 수행하는 핸들러
/// </summary>
public class JsonSkillHandler : IActionHandler
{
    private readonly JsonSkillDefinition _def;
    private readonly HttpClient _http = new();
    private List<LauncherItem>? _cache;
    private DateTime _cacheExpiry = DateTime.MinValue;

    public string? Prefix => _def.Prefix;
    public PluginMetadata Metadata => new(_def.Id, _def.Name, _def.Version, "JSON Skill");

    public JsonSkillHandler(JsonSkillDefinition def)
    {
        _def = def;
        _http.Timeout = TimeSpan.FromSeconds(3);

        // 인증 헤더 설정
        if (def.Credential?.Type == "bearer_token")
        {
            var token = CredentialManager.GetToken(def.Credential.CredentialKey);
            if (!string.IsNullOrEmpty(token))
                _http.DefaultRequestHeaders.Authorization =
                    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
        }
    }

    public async Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        // 캐시 확인
        if (_cache != null && DateTime.Now < _cacheExpiry)
            return _cache;

        if (_def.Request == null || _def.Response == null)
            return Enumerable.Empty<LauncherItem>();

        try
        {
            var url = _def.Request.Url.Replace("{{INPUT}}", Uri.EscapeDataString(query));

            // URL 유효성 검증: http/https 스킴만 허용
            if (!Uri.TryCreate(url, UriKind.Absolute, out var parsedUrl) ||
                (parsedUrl.Scheme != Uri.UriSchemeHttp && parsedUrl.Scheme != Uri.UriSchemeHttps))
            {
                LogService.Error($"[{_def.Name}] 유효하지 않은 URL: {url}");
                return [new LauncherItem("설정 오류", "스킬 URL이 유효하지 않습니다 (http/https만 허용)", null, null)];
            }

            var response = _def.Request.Method.ToUpper() switch
            {
                "POST" => await _http.PostAsync(url, BuildBody(query), ct),
                _ => await _http.GetAsync(url, ct)
            };

            response.EnsureSuccessStatusCode();
            var json = await response.Content.ReadAsStringAsync(ct);
            var items = ParseResults(json);

            // 캐시 저장
            if (_def.Cache?.Ttl > 0)
            {
                _cache = items;
                _cacheExpiry = DateTime.Now.AddSeconds(_def.Cache.Ttl);
            }

            return items;
        }
        catch (TaskCanceledException)
        {
            // 타임아웃 → 캐시 반환
            if (_cache != null)
            {
                LogService.Warn($"[{_def.Name}] API 타임아웃, 캐시 반환");
                return _cache;
            }
            return [new LauncherItem("네트워크 오류", "연결을 확인하세요", null, null)];
        }
        catch (Exception ex)
        {
            LogService.Error($"[{_def.Name}] API 호출 실패: {ex.Message}");
            return [new LauncherItem($"오류: {ex.Message}", _def.Name, null, null)];
        }
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.ActionUrl != null &&
            Uri.TryCreate(item.ActionUrl, UriKind.Absolute, out var uri) &&
            (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
        {
            System.Diagnostics.Process.Start(
                new System.Diagnostics.ProcessStartInfo(item.ActionUrl) { UseShellExecute = true });
        }
        return Task.CompletedTask;
    }

    private HttpContent BuildBody(string query)
    {
        var bodyJson = JsonSerializer.Serialize(_def.Request?.Body)
            .Replace("\"{{INPUT}}\"", $"\"{query}\"");
        return new StringContent(bodyJson, Encoding.UTF8, "application/json");
    }

    private List<LauncherItem> ParseResults(string json)
    {
        var items = new List<LauncherItem>();
        try
        {
            var root = JsonNode.Parse(json);
            if (root == null) return items;

            // resultsPath로 배열 탐색 (dot notation)
            var node = NavigatePath(root, _def.Response!.ResultsPath);
            if (node is not JsonArray arr) return items;

            foreach (var element in arr.Take(10))
            {
                if (element == null) continue;
                var title = NavigatePath(element, _def.Response.TitleField)?.ToString() ?? "(제목 없음)";
                var subtitle = _def.Response.SubtitleField != null
                    ? NavigatePath(element, _def.Response.SubtitleField)?.ToString() ?? ""
                    : "";
                var actionUrl = _def.Response.ActionUrl != null
                    ? NavigatePath(element, _def.Response.ActionUrl)?.ToString()
                    : null;

                items.Add(new LauncherItem(title, subtitle, null, element, actionUrl, Symbols.Cloud));
            }
        }
        catch (Exception ex)
        {
            LogService.Error($"[{_def.Name}] 응답 파싱 실패: {ex.Message}");
        }
        return items;
    }

    private static JsonNode? NavigatePath(JsonNode root, string path)
    {
        var parts = path.Split('.');
        JsonNode? current = root;
        foreach (var part in parts)
        {
            if (current == null) return null;
            // 배열 인덱스 처리: field[0]
            var bracketIdx = part.IndexOf('[');
            if (bracketIdx >= 0)
            {
                var closingIdx = part.IndexOf(']');
                if (closingIdx < 0) return null;  // 잘못된 경로 형식 (예: field[0 )
                var fieldName = part[..bracketIdx];
                var index = int.Parse(part[(bracketIdx + 1)..closingIdx]);
                current = current[fieldName]?[index];
            }
            else
            {
                current = current[part];
            }
        }
        return current;
    }
}

/// <summary>
/// Windows Credential Manager (advapi32.dll)를 사용해 자격증명을 안전하게 저장/조회합니다.
/// DPAPI 기반 암호화로 현재 사용자 계정에 귀속되어 저장됩니다.
/// </summary>
public static class CredentialManager
{
    private const uint CRED_TYPE_GENERIC = 1;
    private const uint CRED_PERSIST_LOCAL_MACHINE = 2;

    [StructLayout(LayoutKind.Sequential)]
    private struct FILETIME { public uint dwLowDateTime; public uint dwHighDateTime; }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct CREDENTIAL
    {
        public uint Flags;
        public uint Type;
        public IntPtr TargetName;
        public IntPtr Comment;
        public FILETIME LastWritten;
        public uint CredentialBlobSize;
        public IntPtr CredentialBlob;
        public uint Persist;
        public uint AttributeCount;
        public IntPtr Attributes;
        public IntPtr TargetAlias;
        public IntPtr UserName;
    }

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CredRead(string target, uint type, uint flags, out IntPtr credential);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CredWrite([In] ref CREDENTIAL userCredential, uint flags);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern void CredFree(IntPtr cred);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CredDelete(string target, uint type, uint flags);

    /// <summary>
    /// Windows Credential Manager에서 토큰을 읽습니다.
    /// 저장된 자격증명이 없으면 환경변수에서 폴백합니다.
    /// </summary>
    public static string? GetToken(string key)
    {
        if (string.IsNullOrEmpty(key)) return null;

        try
        {
            if (CredRead(key, CRED_TYPE_GENERIC, 0, out IntPtr ptr))
            {
                try
                {
                    var cred = Marshal.PtrToStructure<CREDENTIAL>(ptr);
                    if (cred.CredentialBlobSize > 0 && cred.CredentialBlob != IntPtr.Zero)
                        return Marshal.PtrToStringUni(cred.CredentialBlob,
                            (int)cred.CredentialBlobSize / 2);
                }
                finally
                {
                    CredFree(ptr);
                }
            }
        }
        catch (Exception ex)
        {
            LogService.Warn($"Windows Credential Manager 읽기 실패 ({key}): {ex.Message}");
        }

        // 환경변수 폴백 (개발 환경용)
        return Environment.GetEnvironmentVariable(key.ToUpperInvariant());
    }

    /// <summary>
    /// Windows Credential Manager에 토큰을 DPAPI로 암호화하여 저장합니다.
    /// </summary>
    public static void SetToken(string key, string token)
    {
        if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(token)) return;

        var blob = Encoding.Unicode.GetBytes(token);
        var blobPtr = Marshal.AllocHGlobal(blob.Length);
        var targetPtr = Marshal.StringToCoTaskMemUni(key);
        var userPtr = Marshal.StringToCoTaskMemUni(Environment.UserName);

        try
        {
            Marshal.Copy(blob, 0, blobPtr, blob.Length);
            var cred = new CREDENTIAL
            {
                Type = CRED_TYPE_GENERIC,
                TargetName = targetPtr,
                UserName = userPtr,
                CredentialBlobSize = (uint)blob.Length,
                CredentialBlob = blobPtr,
                Persist = CRED_PERSIST_LOCAL_MACHINE,
            };

            if (!CredWrite(ref cred, 0))
                LogService.Error($"토큰 저장 실패: {key}, 오류 코드: {Marshal.GetLastWin32Error()}");
            else
                LogService.Info($"토큰 저장 완료: {key}");
        }
        finally
        {
            Marshal.FreeHGlobal(blobPtr);
            Marshal.FreeCoTaskMem(targetPtr);
            Marshal.FreeCoTaskMem(userPtr);
        }
    }

    /// <summary>
    /// Windows Credential Manager에서 자격증명을 삭제합니다.
    /// </summary>
    public static bool DeleteToken(string key) =>
        !string.IsNullOrEmpty(key) && CredDelete(key, CRED_TYPE_GENERIC, 0);
}
