using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

/// <summary>
/// L22-2: Excel 함수 레퍼런스 핸들러. "xl" 프리픽스로 사용합니다.
///
/// 예: xl                    → 카테고리 목록
///     xl lookup             → 찾기·참조 함수 (VLOOKUP, INDEX, MATCH 등)
///     xl if                 → 논리 함수 (IF, IFS, IFERROR 등)
///     xl sum                → 합산 함수 (SUM, SUMIF, SUMIFS 등)
///     xl count              → 개수 함수 (COUNT, COUNTA, COUNTIF 등)
///     xl text               → 텍스트 함수 (LEFT, RIGHT, MID, TRIM 등)
///     xl date               → 날짜 함수 (TODAY, DATEDIF, EDATE 등)
///     xl math               → 수학 함수 (ROUND, INT, MOD, ABS 등)
///     xl stat               → 통계 함수 (AVERAGE, MAX, MIN, RANK 등)
///     xl <검색어>           → 함수명·설명 검색
/// Enter → 함수 이름 복사.
/// </summary>
public class XlHandler : IActionHandler
{
    public string? Prefix => "xl";

    public PluginMetadata Metadata => new(
        "Excel 함수",
        "Excel 함수 레퍼런스 — 조회·논리·텍스트·날짜·수학·통계",
        "1.0",
        "AX");

    private sealed record XlFunc(string Name, string Syntax, string Description, string Category);

    private static readonly XlFunc[] Funcs =
    [
        // ── 조회·참조 (lookup) ──────────────────────────────────────────────
        new("VLOOKUP",   "VLOOKUP(값, 범위, 열번호, [일치유형])",       "세로 방향 조회 — 열에서 값 검색 후 지정 열 반환", "lookup"),
        new("HLOOKUP",   "HLOOKUP(값, 범위, 행번호, [일치유형])",       "가로 방향 조회 — 행에서 값 검색 후 지정 행 반환", "lookup"),
        new("XLOOKUP",   "XLOOKUP(값, 찾기범위, 반환범위, [없을때])",   "유연한 조회 — 방향 무관, 정확/근사/와일드카드",   "lookup"),
        new("INDEX",     "INDEX(범위, 행번호, [열번호])",                "범위에서 특정 위치의 값 반환",                    "lookup"),
        new("MATCH",     "MATCH(값, 범위, [일치유형])",                  "범위에서 값의 상대 위치(순번) 반환",              "lookup"),
        new("OFFSET",    "OFFSET(기준, 행이동, 열이동, [높이], [너비])","기준 셀에서 이동한 범위 반환",                    "lookup"),
        new("INDIRECT",  "INDIRECT(참조문자열, [A1형식])",               "문자열로 표현된 참조를 실제 참조로 변환",         "lookup"),
        new("CHOOSE",    "CHOOSE(인덱스, 값1, 값2, ...)",                "인덱스 번호에 해당하는 값 반환",                  "lookup"),
        new("ADDRESS",   "ADDRESS(행번호, 열번호, [참조형식])",          "셀 주소 문자열 생성 (예: \"$A$1\")",              "lookup"),

        // ── 논리 (if) ────────────────────────────────────────────────────────
        new("IF",        "IF(조건, 참값, 거짓값)",                       "조건이 참이면 참값, 거짓이면 거짓값 반환",        "if"),
        new("IFS",       "IFS(조건1, 값1, 조건2, 값2, ...)",             "여러 조건을 순서대로 검사하여 첫 번째 참 반환",   "if"),
        new("IFERROR",   "IFERROR(식, 오류시값)",                        "식이 오류이면 오류시값, 아니면 식 결과 반환",     "if"),
        new("IFNA",      "IFNA(식, NA시값)",                             "#N/A 오류 시 NA시값 반환",                        "if"),
        new("AND",       "AND(조건1, 조건2, ...)",                       "모든 조건이 참이면 TRUE",                         "if"),
        new("OR",        "OR(조건1, 조건2, ...)",                        "하나 이상 조건이 참이면 TRUE",                    "if"),
        new("NOT",       "NOT(조건)",                                     "논리값 반전",                                     "if"),
        new("SWITCH",    "SWITCH(식, 값1, 결과1, [값2, 결과2], ...)",    "식을 여러 값과 비교하여 일치하는 결과 반환",      "if"),

        // ── 합산 (sum) ────────────────────────────────────────────────────────
        new("SUM",       "SUM(범위1, [범위2], ...)",                     "합계",                                            "sum"),
        new("SUMIF",     "SUMIF(조건범위, 조건, [합산범위])",             "조건에 맞는 셀의 합계",                           "sum"),
        new("SUMIFS",    "SUMIFS(합산범위, 조건범위1, 조건1, ...)",       "여러 조건에 맞는 셀의 합계",                      "sum"),
        new("SUMPRODUCT","SUMPRODUCT(배열1, [배열2], ...)",              "배열 요소를 곱한 후 합계",                        "sum"),
        new("SUBTOTAL",  "SUBTOTAL(함수번호, 범위)",                     "필터링·숨긴 행 제외한 부분합",                    "sum"),
        new("AGGREGATE", "AGGREGATE(함수번호, 옵션, 범위)",              "오류·숨긴 행 무시하고 집계",                      "sum"),

        // ── 개수 (count) ──────────────────────────────────────────────────────
        new("COUNT",     "COUNT(값1, [값2], ...)",                       "숫자가 있는 셀 개수",                             "count"),
        new("COUNTA",    "COUNTA(값1, [값2], ...)",                      "비어있지 않은 셀 개수",                           "count"),
        new("COUNTBLANK","COUNTBLANK(범위)",                             "빈 셀 개수",                                      "count"),
        new("COUNTIF",   "COUNTIF(범위, 조건)",                          "조건에 맞는 셀 개수",                             "count"),
        new("COUNTIFS",  "COUNTIFS(범위1, 조건1, [범위2, 조건2], ...)", "여러 조건에 맞는 셀 개수",                        "count"),

        // ── 텍스트 (text) ──────────────────────────────────────────────────────
        new("LEFT",      "LEFT(텍스트, 문자수)",                         "왼쪽에서 N자 추출",                               "text"),
        new("RIGHT",     "RIGHT(텍스트, 문자수)",                        "오른쪽에서 N자 추출",                             "text"),
        new("MID",       "MID(텍스트, 시작, 문자수)",                    "중간 N자 추출",                                   "text"),
        new("LEN",       "LEN(텍스트)",                                  "문자 수 반환",                                    "text"),
        new("FIND",      "FIND(찾기, 텍스트, [시작위치])",               "대소문자 구분하여 위치 반환",                     "text"),
        new("SEARCH",    "SEARCH(찾기, 텍스트, [시작위치])",             "대소문자 무시하여 위치 반환",                     "text"),
        new("SUBSTITUTE","SUBSTITUTE(텍스트, 찾기, 바꾸기, [번째])",    "특정 문자열을 다른 문자열로 치환",                "text"),
        new("REPLACE",   "REPLACE(텍스트, 시작, 문자수, 새텍스트)",     "위치 기반으로 문자열 교체",                       "text"),
        new("TRIM",      "TRIM(텍스트)",                                 "앞뒤·중복 공백 제거",                             "text"),
        new("CLEAN",     "CLEAN(텍스트)",                                "인쇄 불가 문자 제거",                             "text"),
        new("UPPER",     "UPPER(텍스트)",                                "대문자로 변환",                                   "text"),
        new("LOWER",     "LOWER(텍스트)",                                "소문자로 변환",                                   "text"),
        new("PROPER",    "PROPER(텍스트)",                               "각 단어 첫 글자만 대문자",                        "text"),
        new("CONCAT",    "CONCAT(텍스트1, 텍스트2, ...)",               "텍스트 연결 (CONCATENATE 최신 버전)",             "text"),
        new("TEXTJOIN",  "TEXTJOIN(구분자, 빈셀무시, 텍스트1, ...)",    "구분자를 포함하여 텍스트 연결",                   "text"),
        new("TEXT",      "TEXT(값, 서식)",                               "숫자를 서식 문자열로 변환 (예: \"#,##0\")",       "text"),
        new("VALUE",     "VALUE(텍스트)",                                "텍스트를 숫자로 변환",                            "text"),
        new("NUMBERVALUE","NUMBERVALUE(텍스트, 소수점, 그룹구분)",      "로캘 독립적 텍스트→숫자 변환",                    "text"),

        // ── 날짜 (date) ──────────────────────────────────────────────────────
        new("TODAY",     "TODAY()",                                       "오늘 날짜 반환",                                  "date"),
        new("NOW",       "NOW()",                                         "현재 날짜와 시간 반환",                           "date"),
        new("DATE",      "DATE(년, 월, 일)",                             "날짜 값 생성",                                    "date"),
        new("DATEDIF",   "DATEDIF(시작일, 종료일, 단위)",                "두 날짜 간 차이 — 단위: Y M D YM YD MD",         "date"),
        new("EDATE",     "EDATE(시작일, 개월수)",                        "N개월 후/전 날짜",                                "date"),
        new("EOMONTH",   "EOMONTH(시작일, 개월수)",                      "N개월 후 월 말일",                                "date"),
        new("WORKDAY",   "WORKDAY(시작일, 일수, [공휴일])",              "영업일 N일 후 날짜",                              "date"),
        new("NETWORKDAYS","NETWORKDAYS(시작일, 종료일, [공휴일])",       "두 날짜 사이 영업일 수",                          "date"),
        new("YEAR",      "YEAR(날짜)",                                    "연도 추출",                                       "date"),
        new("MONTH",     "MONTH(날짜)",                                   "월 추출",                                         "date"),
        new("DAY",       "DAY(날짜)",                                     "일 추출",                                         "date"),
        new("WEEKDAY",   "WEEKDAY(날짜, [반환형식])",                     "요일 번호 반환 (1=일요일)",                       "date"),
        new("WEEKNUM",   "WEEKNUM(날짜, [반환형식])",                     "해당 날짜의 주 번호",                             "date"),

        // ── 수학 (math) ──────────────────────────────────────────────────────
        new("ROUND",     "ROUND(숫자, 자릿수)",                          "반올림",                                          "math"),
        new("ROUNDUP",   "ROUNDUP(숫자, 자릿수)",                        "올림",                                            "math"),
        new("ROUNDDOWN", "ROUNDDOWN(숫자, 자릿수)",                      "내림",                                            "math"),
        new("INT",       "INT(숫자)",                                     "정수 부분만 반환 (내림)",                         "math"),
        new("MOD",       "MOD(숫자, 제수)",                              "나머지 반환",                                     "math"),
        new("ABS",       "ABS(숫자)",                                     "절대값",                                          "math"),
        new("POWER",     "POWER(숫자, 지수)",                             "거듭제곱",                                        "math"),
        new("SQRT",      "SQRT(숫자)",                                    "제곱근",                                          "math"),
        new("CEILING",   "CEILING(숫자, 기준)",                           "기준의 배수로 올림",                              "math"),
        new("FLOOR",     "FLOOR(숫자, 기준)",                             "기준의 배수로 내림",                              "math"),
        new("RAND",      "RAND()",                                         "0~1 사이 난수",                                   "math"),
        new("RANDBETWEEN","RANDBETWEEN(최소, 최대)",                      "정수 난수",                                       "math"),
        new("LARGE",     "LARGE(범위, 순위)",                             "N번째로 큰 값",                                   "math"),
        new("SMALL",     "SMALL(범위, 순위)",                             "N번째로 작은 값",                                 "math"),

        // ── 통계 (stat) ──────────────────────────────────────────────────────
        new("AVERAGE",   "AVERAGE(값1, [값2], ...)",                     "평균",                                            "stat"),
        new("AVERAGEIF", "AVERAGEIF(범위, 조건, [평균범위])",             "조건에 맞는 셀의 평균",                           "stat"),
        new("AVERAGEIFS","AVERAGEIFS(평균범위, 범위1, 조건1, ...)",      "여러 조건에 맞는 셀의 평균",                      "stat"),
        new("MAX",       "MAX(값1, [값2], ...)",                         "최대값",                                          "stat"),
        new("MIN",       "MIN(값1, [값2], ...)",                         "최소값",                                          "stat"),
        new("MAXIFS",    "MAXIFS(최대범위, 조건범위1, 조건1, ...)",       "조건에 맞는 최대값",                              "stat"),
        new("MINIFS",    "MINIFS(최소범위, 조건범위1, 조건1, ...)",       "조건에 맞는 최소값",                              "stat"),
        new("MEDIAN",    "MEDIAN(값1, [값2], ...)",                      "중앙값",                                          "stat"),
        new("MODE",      "MODE(값1, [값2], ...)",                        "최빈값",                                          "stat"),
        new("RANK",      "RANK(숫자, 범위, [정렬])",                      "순위 반환 (0=내림차순)",                          "stat"),
        new("STDEV",     "STDEV(값1, [값2], ...)",                       "표준편차 (샘플)",                                 "stat"),
        new("VAR",       "VAR(값1, [값2], ...)",                         "분산 (샘플)",                                     "stat"),
        new("CORREL",    "CORREL(배열1, 배열2)",                         "두 배열의 상관계수",                              "stat"),
        new("PERCENTILE","PERCENTILE(배열, k)",                          "백분위수 (k: 0~1)",                               "stat"),
    ];

    private static readonly (string Key, string[] Aliases, string Label)[] Categories =
    [
        ("lookup", ["lookup", "find", "조회", "참조", "찾기"],       "조회·참조"),
        ("if",     ["if", "logic", "논리", "조건"],                    "논리·조건"),
        ("sum",    ["sum", "합계", "sumif", "더하기"],                 "합산"),
        ("count",  ["count", "개수", "countif"],                       "개수"),
        ("text",   ["text", "텍스트", "문자", "string"],               "텍스트"),
        ("date",   ["date", "날짜", "time", "시간"],                   "날짜·시간"),
        ("math",   ["math", "수학", "round", "수식"],                  "수학"),
        ("stat",   ["stat", "통계", "average", "max"],                 "통계"),
    ];

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var items = new List<LauncherItem>();

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem("Excel 함수 레퍼런스",
                "카테고리: lookup · if · sum · count · text · date · math · stat",
                null, null, Symbol: "\uE9D2"));

            foreach (var (key, _, label) in Categories)
            {
                var cnt = Funcs.Count(f => f.Category == key);
                items.Add(new LauncherItem($"xl {key}", $"{label}  ({cnt}개)", null, ("copy", $"xl {key}"), Symbol: "\uE9D2"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var kw = q.ToLowerInvariant();

        // 카테고리 일치 확인
        var cat = Categories.FirstOrDefault(c => c.Aliases.Any(a => a == kw || kw.StartsWith(a)));

        if (cat.Key != null)
        {
            var list = Funcs.Where(f => f.Category == cat.Key).ToList();
            items.Add(new LauncherItem($"{cat.Label} 함수 {list.Count}개",
                "Enter: 함수명 복사", null, null, Symbol: "\uE9D2"));
            foreach (var f in list)
                items.Add(FuncItem(f));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 검색어 매칭
        var search = Funcs
            .Where(f => f.Name.Contains(kw, StringComparison.OrdinalIgnoreCase)
                     || f.Description.Contains(kw, StringComparison.OrdinalIgnoreCase)
                     || f.Syntax.Contains(kw, StringComparison.OrdinalIgnoreCase))
            .ToList();

        if (search.Count == 0)
        {
            items.Add(new LauncherItem($"'{q}' 함수를 찾을 수 없습니다",
                "카테고리: lookup · if · sum · count · text · date · math · stat",
                null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        items.Add(new LauncherItem($"'{q}' 검색 결과 {search.Count}개",
            "Enter: 함수명 복사", null, null, Symbol: "\uE9D2"));
        foreach (var f in search)
            items.Add(FuncItem(f));

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("copy", string text))
        {
            try
            {
                System.Windows.Application.Current.Dispatcher.Invoke(
                    () => Clipboard.SetText(text));
                NotificationService.Notify("Excel 함수", "클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }

    private static LauncherItem FuncItem(XlFunc f) =>
        new(f.Name, $"{f.Description}  |  {f.Syntax}",
            null, ("copy", f.Name), Symbol: "\uE9D2");
}
