# team_digest_runtime.py
from __future__ import annotations

import json
import re
import sys
from dataclasses import dataclass, field
from datetime import date, datetime
from pathlib import Path
from typing import Dict, List, Optional, Iterable

try:
    import yaml  # type: ignore
except Exception:  # pragma: no cover
    yaml = None

SECTION_KEYS = ["summary", "decisions", "actions", "risks", "dependencies", "notes"]
HEAD_RE = re.compile(r"^\s{0,3}#{2,6}\s*(.+?)\s*$")  # ## Heading
BULLET_RE = re.compile(r"^\s*[-*]\s+(.+?)\s*$")


@dataclass
class Config:
    timezone: str = "UTC"
    sections: Dict[str, bool] = field(default_factory=lambda: {k: True for k in SECTION_KEYS})

    @staticmethod
    def load(path: Optional[str]) -> "Config":
        if not path:
            return Config()
        p = Path(path)
        if not p.exists():
            return Config()
        if p.suffix.lower() in (".yml", ".yaml"):
            if yaml is None:
                raise RuntimeError("PyYAML is not installed but YAML config was provided.")
            data = yaml.safe_load(p.read_text(encoding="utf-8")) or {}
        elif p.suffix.lower() == ".json":
            data = json.loads(p.read_text(encoding="utf-8"))
        else:
            txt = p.read_text(encoding="utf-8")
            data = yaml.safe_load(txt) if yaml else json.loads(txt)
        cfg = Config()
        secs = data.get("sections") or {}
        for k in SECTION_KEYS:
            if isinstance(secs.get(k), bool):
                cfg.sections[k] = secs[k]
        tz = data.get("timezone")
        if isinstance(tz, str) and tz:
            cfg.timezone = tz
        return cfg


@dataclass
class Digest:
    date_start: Optional[date]
    date_end: Optional[date]
    sections: Dict[str, List[str]] = field(default_factory=lambda: {k: [] for k in SECTION_KEYS})


def _within(d: datetime, since: Optional[date], until: Optional[date]) -> bool:
    dd = d.date()
    if since and dd < since:
        return False
    if until and dd > until:
        return False
    return True


def _files_in_range(input_dir: Path, since: Optional[date], until: Optional[date]) -> List[Path]:
    files: List[Path] = []
    for p in input_dir.rglob("*"):
        if p.is_file() and p.suffix.lower() in (".md", ".txt"):
            try:
                mtime = datetime.fromtimestamp(p.stat().st_mtime)
            except Exception:
                continue
            if _within(mtime, since, until):
                files.append(p)
    return sorted(files)


def _extract_sections(lines: Iterable[str]) -> Dict[str, List[str]]:
    sec_map: Dict[str, List[str]] = {k: [] for k in SECTION_KEYS}
    current_key: Optional[str] = None

    def normalize_heading(h: str) -> Optional[str]:
        h = h.strip().lower()
        for k in SECTION_KEYS:
            if k in h:
                return k
        return None

    for raw in lines:
        m_head = HEAD_RE.match(raw)
        if m_head:
            current_key = normalize_heading(m_head.group(1))
            continue

        m_bullet = BULLET_RE.match(raw)
        if m_bullet:
            text = m_bullet.group(1).strip()
            if current_key and current_key in sec_map:
                sec_map[current_key].append(text)
            else:
                sec_map["notes"].append(text)
            continue

        s = raw.strip()
        if s and current_key in SECTION_KEYS:
            sec_map[current_key].append(s)

    return sec_map


def _render_md(d: Digest) -> str:
    ds = d.date_start.isoformat() if d.date_start else "n/a"
    de = d.date_end.isoformat() if d.date_end else "n/a"
    out = [f"# Team Digest ({ds} - {de})", ""]
    for k in SECTION_KEYS:
        items = d.sections[k]
        title = k.capitalize()
        out.append(f"## {title}")
        if items:
            for it in items:
                out.append(f"- {it}")
        else:
            out.append(f"_No {k}._")
        out.append("")
    return "\n".join(out).rstrip() + "\n"


def _render_json(d: Digest) -> str:
    payload = {
        "date_start": d.date_start.isoformat() if d.date_start else None,
        "date_end": d.date_end.isoformat() if d.date_end else None,
        "sections": d.sections,
    }
    return json.dumps(payload, indent=2)


def generate_digest(
    *,
    fmt: str,
    config_path: Optional[str],
    since: Optional[date],
    until: Optional[date],
    input_dir: Optional[str],
) -> str:
    cfg = Config.load(config_path)

    date_start, date_end = since, until
    sections_accum: Dict[str, List[str]] = {k: [] for k in SECTION_KEYS}

    if input_dir and input_dir != "-":
        base = Path(input_dir)
        files = _files_in_range(base, since, until)
        if files and (date_start is None or date_end is None):
            mts = [datetime.fromtimestamp(p.stat().st_mtime).date() for p in files]
            date_start = date_start or min(mts)
            date_end = date_end or max(mts)

        for p in files:
            try:
                lines = p.read_text(encoding="utf-8").splitlines()
            except Exception:
                continue
            sec_map = _extract_sections(lines)
            for k in SECTION_KEYS:
                if cfg.sections.get(k, True):
                    sections_accum[k].extend(sec_map[k])
    else:
        text = sys.stdin.read()
        sec_map = _extract_sections(text.splitlines())
        for k in SECTION_KEYS:
            if cfg.sections.get(k, True):
                sections_accum[k].extend(sec_map[k])

    digest = Digest(date_start=date_start, date_end=date_end, sections=sections_accum)
    return _render_md(digest) if fmt == "md" else _render_json(digest)
