using System.IO;
using System.Runtime.InteropServices;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// Everything SDK를 이용한 초고속 파일 검색 핸들러.
/// "es" 프리픽스로 사용합니다.
///
/// 예: es report     → "report" 파일명 검색
///     es *.xlsx     → 엑셀 파일 검색
///     es 회의록      → 한글 파일명 검색
///
/// Everything이 설치되어 있지 않으면 자동으로 비활성화됩니다.
/// </summary>
public class EverythingHandler : IActionHandler
{
    public string? Prefix => "es";

    public PluginMetadata Metadata => new(
        "EverythingSearch",
        "Everything 초고속 파일 검색 — es [키워드]",
        "1.0",
        "AX");

    // ─── Everything SDK P/Invoke ─────────────────────────────────────────────

    private const int EVERYTHING_OK = 0;
    private const int EVERYTHING_REQUEST_FILE_NAME = 0x00000001;
    private const int EVERYTHING_REQUEST_PATH = 0x00000002;
    private const int EVERYTHING_REQUEST_SIZE = 0x00000010;

    [DllImport("Everything64.dll", CharSet = CharSet.Unicode)]
    private static extern uint Everything_SetSearchW(string lpSearchString);

    [DllImport("Everything64.dll")]
    private static extern void Everything_SetMax(uint dwMax);

    [DllImport("Everything64.dll")]
    private static extern void Everything_SetRequestFlags(uint dwRequestFlags);

    [DllImport("Everything64.dll")]
    private static extern bool Everything_QueryW(bool bWait);

    [DllImport("Everything64.dll")]
    private static extern uint Everything_GetNumResults();

    [DllImport("Everything64.dll")]
    private static extern uint Everything_GetLastError();

    [DllImport("Everything64.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr Everything_GetResultFullPathNameW(uint nIndex, IntPtr lpString, uint nMaxCount);

    [DllImport("Everything64.dll", CharSet = CharSet.Unicode)]
    private static extern void Everything_GetResultSize(uint nIndex, out long lpFileSize);

    [DllImport("Everything64.dll")]
    private static extern uint Everything_GetMajorVersion();

    // ─── 상태 ────────────────────────────────────────────────────────────────

    private bool? _isAvailable;

    private bool IsAvailable
    {
        get
        {
            if (_isAvailable.HasValue) return _isAvailable.Value;
            try
            {
                Everything_GetMajorVersion();
                _isAvailable = true;
            }
            catch (Exception)
            {
                _isAvailable = false;
            }
            return _isAvailable.Value;
        }
    }

    // ─── IActionHandler ──────────────────────────────────────────────────────

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        if (!IsAvailable)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem(
                    "Everything이 설치되어 있지 않습니다",
                    "voidtools.com에서 Everything을 설치하면 초고속 파일 검색을 사용할 수 있습니다",
                    null, null, Symbol: Symbols.Warning)
            });
        }

        var q = query.Trim();
        if (string.IsNullOrWhiteSpace(q))
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem(
                    "Everything 파일 검색",
                    "검색어를 입력하세요 — 파일명, 확장자(*.xlsx), 경로 일부 등",
                    null, null, Symbol: Symbols.Search)
            });
        }

        try
        {
            Everything_SetSearchW(q);
            Everything_SetMax(30);
            Everything_SetRequestFlags(EVERYTHING_REQUEST_FILE_NAME | EVERYTHING_REQUEST_PATH | EVERYTHING_REQUEST_SIZE);

            if (!Everything_QueryW(true))
            {
                return Task.FromResult<IEnumerable<LauncherItem>>(new[]
                {
                    new LauncherItem(
                        "Everything 검색 실패",
                        $"오류 코드: {Everything_GetLastError()} — Everything 서비스가 실행 중인지 확인하세요",
                        null, null, Symbol: Symbols.Warning)
                });
            }

            var count = Everything_GetNumResults();
            if (count == 0)
            {
                return Task.FromResult<IEnumerable<LauncherItem>>(new[]
                {
                    new LauncherItem(
                        $"검색 결과 없음: {q}",
                        "다른 키워드나 와일드카드(*.xlsx)를 시도해 보세요",
                        null, null, Symbol: Symbols.Info)
                });
            }

            var items = new List<LauncherItem>();
            var buffer = Marshal.AllocHGlobal(520 * 2); // MAX_PATH * sizeof(wchar_t)
            try
            {
                for (uint i = 0; i < count && i < 30; i++)
                {
                    Everything_GetResultFullPathNameW(i, buffer, 520);
                    var fullPath = Marshal.PtrToStringUni(buffer) ?? "";

                    Everything_GetResultSize(i, out var fileSize);
                    var fileName = Path.GetFileName(fullPath);
                    var dirPath = Path.GetDirectoryName(fullPath) ?? "";
                    var sizeStr = fileSize > 0 ? FormatSize(fileSize) : "";
                    var subtitle = string.IsNullOrEmpty(sizeStr) ? dirPath : $"{sizeStr}  ·  {dirPath}";

                    var symbol = Directory.Exists(fullPath) ? Symbols.Folder : GetFileSymbol(fullPath);

                    items.Add(new LauncherItem(fileName, subtitle, null, fullPath, Symbol: symbol));
                }
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }

            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }
        catch (Exception ex)
        {
            LogService.Warn($"Everything 검색 오류: {ex.Message}");
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem("Everything 검색 오류", ex.Message, null, null, Symbol: Symbols.Warning)
            });
        }
    }

    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is not string path || string.IsNullOrEmpty(path)) return;

        try
        {
            if (Directory.Exists(path))
            {
                System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
                {
                    FileName = path,
                    UseShellExecute = true,
                });
            }
            else if (File.Exists(path))
            {
                System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
                {
                    FileName = path,
                    UseShellExecute = true,
                });
            }
        }
        catch (Exception ex)
        {
            LogService.Warn($"Everything 결과 열기 실패: {ex.Message}");
        }

        await Task.CompletedTask;
    }

    // ─── 헬퍼 ────────────────────────────────────────────────────────────────

    private static string FormatSize(long bytes)
    {
        if (bytes < 1024) return $"{bytes} B";
        if (bytes < 1024 * 1024) return $"{bytes / 1024.0:F1} KB";
        if (bytes < 1024 * 1024 * 1024) return $"{bytes / (1024.0 * 1024):F1} MB";
        return $"{bytes / (1024.0 * 1024 * 1024):F2} GB";
    }

    private static string GetFileSymbol(string path)
    {
        var ext = Path.GetExtension(path).ToLowerInvariant();
        return ext switch
        {
            ".xlsx" or ".xls" or ".csv" => Symbols.Excel,
            ".docx" or ".doc" => Symbols.Word,
            ".pptx" or ".ppt" => Symbols.Slides,
            ".pdf" => Symbols.Pdf,
            ".png" or ".jpg" or ".jpeg" or ".gif" or ".bmp" or ".svg" => Symbols.Image,
            ".mp4" or ".avi" or ".mkv" or ".mov" => Symbols.Video,
            ".mp3" or ".wav" or ".flac" => Symbols.Music,
            ".zip" or ".rar" or ".7z" or ".tar" or ".gz" => Symbols.Archive,
            ".exe" or ".msi" => Symbols.App,
            ".cs" or ".py" or ".js" or ".ts" or ".java" or ".cpp" or ".c" or ".go" => Symbols.Code,
            ".json" or ".xml" or ".yaml" or ".yml" => Symbols.Config,
            ".txt" or ".md" or ".log" => Symbols.TextFile,
            ".html" or ".htm" or ".css" => Symbols.Web,
            _ => Symbols.File,
        };
    }
}
