"""
반복 연차 패턴 파싱 + 일자 확장.

지원 패턴:
    - 'weekly:friday'           — 매주 금요일
    - 'weekly:mon,wed,fri'      — 매주 월·수·금
    - 'biweekly:friday'         — 격주 금요일 (start_date 기준)
    - 'monthly:15'              — 매월 15일 (해당 월에 그 일이 없으면 스킵)

반복 인스턴스는 DB에 영속화하지 않고 호출 시점에 expand_for_range()로 펼친다.
같은 날짜에 leave_records(구체 인스턴스)가 이미 있으면 호출자가 합산 로직 책임.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from typing import List, Dict, Optional

from core.i18n import tr


_WEEKDAY_MAP = {
    'mon': 0, 'monday': 0,
    'tue': 1, 'tuesday': 1,
    'wed': 2, 'wednesday': 2,
    'thu': 3, 'thursday': 3,
    'fri': 4, 'friday': 4,
    'sat': 5, 'saturday': 5,
    'sun': 6, 'sunday': 6,
}
_WEEKDAY_KEYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']


@dataclass
class Occurrence:
    """반복 패턴이 펼친 한 인스턴스."""
    date: date
    leave_type: str
    days: float
    pattern: str
    memo: str = ''
    recurring_id: Optional[int] = None


def _parse_pattern(pattern: str):
    """('weekly'|'biweekly'|'monthly', 추가정보) 튜플 반환. 잘못된 패턴은 None."""
    if not pattern or ':' not in pattern:
        return None
    kind, rest = pattern.split(':', 1)
    kind = kind.strip().lower()
    rest = rest.strip().lower()
    if kind in ('weekly', 'biweekly'):
        days = [d.strip() for d in rest.split(',') if d.strip()]
        weekdays = [_WEEKDAY_MAP[d] for d in days if d in _WEEKDAY_MAP]
        if not weekdays:
            return None
        return (kind, weekdays)
    if kind == 'monthly':
        try:
            day_of_month = int(rest)
            if 1 <= day_of_month <= 31:
                return (kind, day_of_month)
        except ValueError:
            return None
    return None


def matches(rec: Dict, target_date: date) -> bool:
    """단일 패턴 rec이 target_date에 매치되는지."""
    start = _parse_date(rec.get('start_date'))
    end = _parse_date(rec.get('end_date'))
    if start is None:
        return False
    if target_date < start:
        return False
    if end is not None and target_date > end:
        return False

    parsed = _parse_pattern(rec.get('pattern', ''))
    if parsed is None:
        return False
    kind, info = parsed

    if kind == 'weekly':
        return target_date.weekday() in info

    if kind == 'biweekly':
        if target_date.weekday() not in info:
            return False
        # start_date의 주(월요일 기준)와 target의 주의 격주 여부
        weeks = (target_date - start).days // 7
        return weeks % 2 == 0

    if kind == 'monthly':
        return target_date.day == info

    return False


def expand_for_range(records: List[Dict], start: date, end: date) -> List[Occurrence]:
    """여러 반복 패턴을 [start, end] 범위에서 펼친다.

    반환은 날짜 오름차순. 같은 날 여러 패턴이 매치되면 모두 포함.
    """
    out: List[Occurrence] = []
    if start > end:
        return out
    cur = start
    while cur <= end:
        for r in records:
            try:
                if matches(r, cur):
                    out.append(Occurrence(
                        date=cur,
                        leave_type=r.get('leave_type') or '연차',
                        days=float(r.get('days') or 0),
                        pattern=r.get('pattern', ''),
                        memo=r.get('memo') or '',
                        recurring_id=r.get('id'),
                    ))
            except Exception:
                # 잘못된 패턴 1개가 전체를 망치지 않도록
                continue
        cur += timedelta(days=1)
    return out


def expand_for_date(records: List[Dict], target_date: date) -> List[Occurrence]:
    """단일 날짜에 매치되는 인스턴스만."""
    return expand_for_range(records, target_date, target_date)


def _parse_date(s: Optional[str]) -> Optional[date]:
    if not s:
        return None
    try:
        return datetime.strptime(s, '%Y-%m-%d').date()
    except (ValueError, TypeError):
        return None


def describe_pattern(pattern: str) -> str:
    """사용자에게 보여줄 패턴 설명."""
    parsed = _parse_pattern(pattern)
    if parsed is None:
        return pattern
    kind, info = parsed
    if kind in ('weekly', 'biweekly'):
        names = [tr(f'label.weekday_{_WEEKDAY_KEYS[w]}') for w in info]
        prefix = tr('recurring.weekly') if kind == 'weekly' else tr('recurring.biweekly')
        return tr('recurring.pattern_weekly', prefix=prefix, weekdays=','.join(names))
    if kind == 'monthly':
        return tr('recurring.pattern_monthly', day=info)
    return pattern
