"""
Markdown 表格解析器。

从 markitdown 或 OCR 输出的 Markdown 内容中提取结构化日程事件。
移植自 skills/schedule-reminder/scripts/parse_schedule.py 的列名识别逻辑，
输出格式与 add_event.py / import_events.py 兼容。

用法：
    from services.table_parser import parse_events_from_markdown
    events = parse_events_from_markdown(md_content)
    # events: [{"summary": "...", "date": "YYYY-MM-DD", "start": "HH:MM", ...}, ...]
"""

from __future__ import annotations

import re
import sys
from datetime import date, datetime
from pathlib import Path
from typing import Optional

sys.path.insert(0, str(Path(__file__).parent.parent))

from services.datetime_utils import current_year

# ─────────────────────────── 列名别名表 ──────────────────────────
# 各字段的候选列名（中英文、简写、Excel 常见变体）

COLUMN_ALIASES: dict[str, set[str]] = {
    "date": {
        "日期", "date", "Date", "DATE", "日", "活动日期", "会议日期",
        "事件日期", "日程日期", "计划日期", "时间",
    },
    "start": {
        "开始时间", "开始", "start", "Start", "START", "开始时刻",
        "起始时间", "from", "From", "时间开始", "起始", "开始时间段",
    },
    "end": {
        "结束时间", "结束", "end", "End", "END", "结束时刻",
        "to", "To", "时间结束", "终止",
    },
    "title": {
        "事项", "标题", "title", "Title", "TITLE", "内容", "会议", "活动",
        "事件", "subject", "Subject", "name", "Name", "任务", "描述",
        "会议名称", "活动名称", "事项名称", "议题", "主题", "项目",
        "meeting", "Meeting", "event", "Event", "topic", "Topic",
    },
    "location": {
        "地点", "location", "Location", "LOCATION", "地址", "会议室",
        "venue", "Venue", "place", "Place", "room", "Room", "场所", "场地",
    },
    "participants": {
        "参与者", "人员", "participants", "Participants", "与会者", "成员",
        "attendees", "Attendees", "负责人", "参会人", "参会人员",
    },
    "advance": {
        "提前(分钟)", "提前提醒", "提前", "advance", "Advance",
        "reminder", "Reminder", "提前时间", "提醒时间", "提前分钟",
    },
    "note": {
        "备注", "note", "Note", "NOTE", "notes", "Notes",
        "说明", "remark", "Remark", "comments", "附注", "补充",
    },
}

_DEFAULT_DURATION_MINUTES = 60
_DEFAULT_ADVANCE_MINUTES  = 15


# ─────────────────────────── 列名检测 ────────────────────────────

def detect_column_mapping(
    header_cells: list[str],
    user_map: dict[str, str] | None = None,
) -> dict[str, int]:
    """
    根据表头行自动推断字段→列索引映射。

    优先级：用户自定义 > 精确别名匹配 > 子串匹配。

    Args:
        header_cells: Markdown 表头各列文字列表
        user_map:     自定义映射 {"自定义列名": "字段名", ...}

    Returns:
        {"title": 0, "date": 1, "start": 2, ...}
    """
    mapping: dict[str, int] = {}
    used_indices: set[int]  = set()

    # 用户自定义（最高优先级）
    if user_map:
        for i, cell in enumerate(header_cells):
            cell_clean = cell.strip()
            if cell_clean in user_map:
                field = user_map[cell_clean]
                if field not in mapping:
                    mapping[field] = i
                    used_indices.add(i)

    # 精确匹配
    for field, aliases in COLUMN_ALIASES.items():
        if field in mapping:
            continue
        for i, cell in enumerate(header_cells):
            if i in used_indices:
                continue
            if cell.strip() in aliases:
                mapping[field] = i
                used_indices.add(i)
                break

    # 子串匹配（兜底）
    for field, aliases in COLUMN_ALIASES.items():
        if field in mapping:
            continue
        for i, cell in enumerate(header_cells):
            if i in used_indices:
                continue
            cell_lower = cell.strip().lower()
            if any(a.lower() in cell_lower or cell_lower in a.lower()
                   for a in aliases):
                mapping[field] = i
                used_indices.add(i)
                break

    return mapping


# ─────────────────────────── 时间规范化 ──────────────────────────

def normalize_date(raw: str) -> Optional[str]:
    """
    将各种日期格式规范化为 YYYY-MM-DD，失败返回 None。
    支持：2026-04-01 / 2026/04/01 / 04-01 / 4/1（补全年份）
    """
    raw = raw.strip()
    for fmt in ("%Y-%m-%d", "%Y/%m/%d"):
        try:
            return datetime.strptime(raw, fmt).strftime("%Y-%m-%d")
        except ValueError:
            pass
    for fmt in ("%m-%d", "%m/%d"):
        try:
            d = datetime.strptime(raw, fmt)
            return d.replace(year=current_year()).strftime("%Y-%m-%d")
        except ValueError:
            pass
    return None


def normalize_time(raw: str) -> Optional[str]:
    """
    将时间字符串规范化为 HH:MM，失败返回 None。
    支持：14:30 / 14:30:00 / 9:05
    """
    raw = raw.strip().replace("：", ":")
    m = re.match(r"(\d{1,2}):(\d{2})", raw)
    if m:
        return f"{int(m.group(1)):02d}:{int(m.group(2)):02d}"
    return None


def add_minutes_to_time(time_str: str, minutes: int) -> str:
    """HH:MM 加 N 分钟，返回 HH:MM（不跨日边界处理）。"""
    h, m = map(int, time_str.split(":"))
    total = h * 60 + m + minutes
    return f"{(total // 60) % 24:02d}:{total % 60:02d}"


# ─────────────────────────── 表格解析 ────────────────────────────

def _split_md_row(line: str) -> list[str]:
    """将 Markdown 表格行拆分为单元格列表（去掉首尾 |）。"""
    cells = line.strip().strip("|").split("|")
    return [c.strip() for c in cells]


def _is_separator_row(line: str) -> bool:
    """判断是否为 Markdown 表格分隔行（如 |---|---|）。"""
    stripped = line.strip()
    # 去掉首尾 | 后，只含 -、:、|、空格
    inner = stripped.strip("|")
    return bool(inner) and bool(re.match(r"^[\s\-:|]+$", inner))


def parse_events_from_markdown(
    content: str,
    user_map: dict[str, str] | None = None,
    default_advance: int = _DEFAULT_ADVANCE_MINUTES,
    default_duration: int = _DEFAULT_DURATION_MINUTES,
) -> list[dict]:
    """
    从 Markdown 内容中提取所有日程事件。

    每个事件输出结构：
        {
            "summary":  str,           # 必填
            "date":     "YYYY-MM-DD",  # 必填
            "start":    "HH:MM",       # 必填
            "end":      "HH:MM",       # 可选
            "location": str,           # 可选
            "description": str,        # 备注
            "remind":   int,           # 提醒分钟数（默认 15）
        }

    跳过：title/date/start 任一缺失或解析失败的行。
    """
    events: list[dict] = []
    lines   = content.splitlines()
    i       = 0

    while i < len(lines):
        line = lines[i]

        # 找到一个表格的表头行
        if not line.strip().startswith("|"):
            i += 1
            continue

        header_cells = _split_md_row(line)

        # 下一行应为分隔行
        if i + 1 >= len(lines) or not _is_separator_row(lines[i + 1]):
            i += 1
            continue

        col_map = detect_column_mapping(header_cells, user_map)

        # title / date / start 是必需字段，至少要有 title
        if "title" not in col_map:
            i += 2
            continue

        # 读数据行
        i += 2
        while i < len(lines) and lines[i].strip().startswith("|"):
            cells = _split_md_row(lines[i])
            i += 1

            def _cell(field: str) -> str:
                idx = col_map.get(field)
                if idx is None or idx >= len(cells):
                    return ""
                return cells[idx].strip()

            title    = _cell("title")
            date_raw = _cell("date")
            start_raw= _cell("start")

            if not title or title in ("-", ""):
                continue

            date_str  = normalize_date(date_raw)  if date_raw  else None
            start_str = normalize_time(start_raw) if start_raw else None

            if not date_str or not start_str:
                continue

            end_raw = _cell("end")
            end_str = normalize_time(end_raw) if end_raw else None
            if not end_str:
                end_str = add_minutes_to_time(start_str, default_duration)

            advance_raw = _cell("advance")
            try:
                remind = int(advance_raw) if advance_raw else default_advance
            except ValueError:
                remind = default_advance

            note     = _cell("note")
            location = _cell("location")
            parts_raw = _cell("participants")
            description = " | ".join(filter(None, [note, parts_raw]))

            events.append({
                "summary":     title,
                "date":        date_str,
                "start":       start_str,
                "end":         end_str,
                "location":    location,
                "description": description,
                "remind":      remind,
            })

    return events
