# src/team_digest/team_email_digest.py
from __future__ import annotations

import argparse
import sys
import re
import shutil
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable, List, Tuple, Optional
import datetime as dt

from importlib.resources import files, as_file
from contextlib import ExitStack

# Single source of truth for version
try:
    from . import __version__
except Exception:
    __version__ = "0+unknown"


# -------------------------
# Utilities
# -------------------------

_DATE_RX = re.compile(r"notes-(\d{4}-\d{2}-\d{2})\.md$", re.I)


def _iter_log_files(logs_dir: Path) -> Iterable[Path]:
    if not logs_dir.exists():
        return []
    for p in sorted(logs_dir.glob("notes-*.md")):
        if _DATE_RX.search(p.name):
            yield p


def _parse_md_section(lines: List[str], header: str) -> List[str]:
    """Extract bullet lines from a '## Header' section."""
    out: List[str] = []
    in_section = False
    hdr_re = re.compile(rf"^##\s*{re.escape(header)}\s*$", re.I)
    for line in lines:
        if hdr_re.match(line.strip()):
            in_section = True
            continue
        if in_section and line.startswith("## "):
            break
        if in_section:
            out.append(line.rstrip("\n"))
    # bullets only
    return [ln for ln in out if ln.strip().startswith("-") or ln.strip().startswith("•")]


def _group_actions_by_priority(bullets: Iterable[str]) -> Tuple[List[str], List[str], List[str]]:
    high: List[str] = []
    med: List[str] = []
    low: List[str] = []
    for b in bullets:
        sb = b.strip()
        tag = sb.lower()
        if "[high]" in tag:
            high.append(sb)
        elif "[medium]" in tag:
            med.append(sb)
        elif "[low]" in tag:
            low.append(sb)
        else:
            # untagged -> medium by default
            med.append(sb)
    return high, med, low


def _footer() -> str:
    return "\n---\n_Digest generated by team-digest (v%s) — https://pypi.org/project/team-digest/_\n" % (__version__,)


def _read_md(path: Path) -> List[str]:
    return path.read_text(encoding="utf-8").splitlines()


# -------------------------
# Renderers (daily/weekly/monthly)
# -------------------------

def render_daily(log_path: Path, group_actions: bool = True) -> str:
    if not log_path.exists():
        return f"# Team Digest ({log_path.stem.replace('notes-','')})\n\n_No log for {log_path.stem.replace('notes-','')} in {log_path.parent.name}_\n" + _footer()

    lines = _read_md(log_path)
    date = _DATE_RX.search(log_path.name).group(1) if _DATE_RX.search(log_path.name) else "Unknown"
    summary = _parse_md_section(lines, "Summary")
    decisions = _parse_md_section(lines, "Decisions")
    actions = _parse_md_section(lines, "Actions")
    risks = _parse_md_section(lines, "Risks")
    deps = _parse_md_section(lines, "Dependencies")
    notes = _parse_md_section(lines, "Notes")

    out: List[str] = [f"# Team Digest ({date})", ""]
    out.append("## Summary")
    out.extend(summary or ["_No summary._", ""])
    out.append("")
    out.append("## Decisions")
    out.extend(decisions or ["_No decisions._", ""])
    out.append("")

    out.append("## Actions")
    if group_actions:
        hi, me, lo = _group_actions_by_priority(actions)
        if not (hi or me or lo):
            out.append("_No actions._")
        else:
            if hi:
                out.append("### High priority")
                out.extend(hi)
            if me:
                out.append("### Medium priority")
                out.extend(me)
            if lo:
                out.append("### Low priority")
                out.extend(lo)
    else:
        out.extend(actions or ["_No actions._"])
    out.append("")

    out.append("## Risks")
    out.extend(risks or ["_No risks._"])
    out.append("")
    out.append("## Dependencies")
    out.extend(deps or ["_No dependencies._"])
    out.append("")
    out.append("## Notes")
    out.extend(notes or ["_No notes._"])
    out.append("")
    out.append(_footer())
    return "\n".join(out)


def render_range(logs_dir: Path, start: dt.date, end: dt.date, title: str, group_actions: bool = True,
                 emit_kpis: bool = False, owner_breakdown: bool = False) -> str:
    matched: List[Tuple[dt.date, Path]] = []
    for p in _iter_log_files(logs_dir):
        m = _DATE_RX.search(p.name)
        if not m:
            continue
        d = dt.date.fromisoformat(m.group(1))
        if start <= d <= end:
            matched.append((d, p))

    matched.sort(key=lambda t: t[0])
    actions_all: List[str] = []
    summaries: List[str] = []
    decisions_all: List[str] = []
    risks_all: List[str] = []
    deps_all: List[str] = []
    notes_all: List[str] = []

    for _, p in matched:
        lines = _read_md(p)
        summaries += _parse_md_section(lines, "Summary")
        decisions_all += _parse_md_section(lines, "Decisions")
        a = _parse_md_section(lines, "Actions")
        actions_all += a
        risks_all += _parse_md_section(lines, "Risks")
        deps_all += _parse_md_section(lines, "Dependencies")
        notes_all += _parse_md_section(lines, "Notes")

    out: List[str] = [f"# {title}", "", f"_Range: {start} → {end} | Source: {logs_dir.name} | Days matched: {len(matched)} | Actions: {len(actions_all)}_", ""]
    if emit_kpis:
        hi, me, lo = _group_actions_by_priority(actions_all)
        owners = {}
        ow_re = re.compile(r"\]\s+([A-Za-z \.-]+)\s+to\s+", re.I)  # naive "Owner to do X"
        for b in actions_all:
            tag = b.lower()
            owner = None
            m = ow_re.search(b)
            if m:
                owner = m.group(1).strip()
            owner = owner or "Unassigned"
            bucket = "medium"
            if "[high]" in tag:
                bucket = "high"
            elif "[low]" in tag:
                bucket = "low"
            dct = owners.setdefault(owner, {"high": 0, "medium": 0, "low": 0})
            dct[bucket] += 1

        out.append("## Executive KPIs")
        out.append(f"- **Actions:** {len(actions_all)} (High: {len(hi)}, Medium: {len(me)}, Low: {len(lo)})")
        out.append(f"- **Decisions:** {len(decisions_all)}   ·   **Risks:** {len(risks_all)}")
        out.append(f"- **Owners:** {len(owners)}   ·   **Days with notes:** {len(matched)}")
        out.append("")
        if owner_breakdown and owners:
            out.append("#### Owner breakdown (top)")
            out.append("| Owner | High | Medium | Low | Total |")
            out.append("|:------|----:|------:|---:|-----:|")
            for name, counts in sorted(owners.items(), key=lambda kv: (-(kv[1]["high"]), -(kv[1]["medium"]), -(kv[1]["low"]), kv[0].lower())):
                total = counts["high"] + counts["medium"] + counts["low"]
                out.append(f"| {name} | {counts['high']} | {counts['medium']} | {counts['low']} | **{total}** |")
            out.append("")

    # Sections
    out.append("## Summary")
    out.extend(summaries or ["_No summary._"])
    out.append("")
    out.append("## Decisions")
    out.extend(decisions_all or ["_No decisions._"])
    out.append("")
    out.append("## Actions")
    if group_actions:
        hi, me, lo = _group_actions_by_priority(actions_all)
        if not (hi or me or lo):
            out.append("_No actions._")
        else:
            if hi:
                out.append("### High priority")
                out.extend(hi)
            if me:
                out.append("### Medium priority")
                out.extend(me)
            if lo:
                out.append("### Low priority")
                out.extend(lo)
    else:
        out.extend(actions_all or ["_No actions._"])
    out.append("")
    out.append("## Risks")
    out.extend(risks_all or ["_No risks._"])
    out.append("")
    out.append("## Dependencies")
    out.extend(deps_all or ["_No dependencies._"])
    out.append("")
    out.append("## Notes")
    out.extend(notes_all or ["_No notes._"])
    out.append("")
    out.append(_footer())
    return "\n".join(out)


# -------------------------
# New: init + doctor
# -------------------------

def cmd_init(args: argparse.Namespace) -> int:
    target = Path(args.to).resolve()
    target.mkdir(parents=True, exist_ok=True)

    # locate packaged examples
    es = ExitStack()
    try:
        pkg_root = files("team_digest").joinpath("examples")
        examples_fs = es.enter_context(as_file(pkg_root))
        src = Path(examples_fs)
        # copy into target/examples (if --into-examples), otherwise into target/
        dest = target
        # Prefer placing exactly the content of examples/ into target/
        if any(dest.iterdir()) and not args.force:
            print(f"[init] Destination '{dest}' is not empty. Use --force to overwrite.", file=sys.stderr)
            return 2
        # Copy everything inside examples/ into dest
        for sub in src.iterdir():
            dst = dest / sub.name
            if dst.exists() and args.force:
                if dst.is_file():
                    dst.unlink()
                else:
                    shutil.rmtree(dst)
            if sub.is_file():
                shutil.copy2(sub, dst)
            else:
                shutil.copytree(sub, dst, dirs_exist_ok=True)
        print(f"[init] Scaffolding complete → {dest}")
        print("[init] Try:")
        print("  team-digest weekly --logs-dir logs --start 2025-10-13 --end 2025-10-19 --output outputs/weekly.md --group-actions --emit-kpis --owner-breakdown")
        return 0
    finally:
        es.close()


def cmd_doctor(args: argparse.Namespace) -> int:
    logs_dir = Path(args.logs_dir).resolve()
    files_found = list(_iter_log_files(logs_dir))
    print(f"[doctor] logs_dir={logs_dir}")
    if not files_found:
        print("[doctor] No matching 'notes-YYYY-MM-DD.md' files found.", file=sys.stderr)
        return 2
    dates = []
    for p in files_found:
        m = _DATE_RX.search(p.name)
        if m:
            dates.append(dt.date.fromisoformat(m.group(1)))
    dates.sort()
    print(f"[doctor] files={len(files_found)} first={dates[0]} last={dates[-1]}")
    return 0


# -------------------------
# CLI
# -------------------------

def main(argv: Optional[List[str]] = None) -> int:
    parser = argparse.ArgumentParser(
        prog="team-digest",
        description="Generate Daily/Weekly/Monthly digests from logs."
    )
    parser.add_argument("-V", "--version", action="version", version=f"team-digest {__version__}")

    sub = parser.add_subparsers(dest="cmd", required=True)

    # daily
    p_d = sub.add_parser("daily", help="Build a daily digest for a single date")
    p_d.add_argument("--logs-dir", required=True, help="Path to Markdown logs directory")
    p_d.add_argument("--date", required=True, help="Date YYYY-MM-DD")
    p_d.add_argument("--output", "-o", required=True, help="Output file (.md or .json)")
    p_d.add_argument("--group-actions", action="store_true", help="Group actions by priority/owner")
    p_d.set_defaults(_fn=lambda a: print(render_daily(Path(a.logs_dir) / f"notes-{a.date}.md", group_actions=a.group_actions)))

    # weekly
    p_w = sub.add_parser("weekly", help="Build a digest for a date range (inclusive)")
    p_w.add_argument("--logs-dir", required=True)
    p_w.add_argument("--start", required=True)
    p_w.add_argument("--end", required=True)
    p_w.add_argument("--output", "-o", required=True)
    p_w.add_argument("--group-actions", action="store_true")
    p_w.add_argument("--emit-kpis", action="store_true")
    p_w.add_argument("--owner-breakdown", action="store_true")
    p_w.set_defaults(_fn=lambda a: print(render_range(
        Path(a.logs_dir), dt.date.fromisoformat(a.start), dt.date.fromisoformat(a.end),
        f"Team Digest ({a.start} - {a.end})",
        group_actions=a.group_actions, emit_kpis=a.emit_kpis, owner_breakdown=a.owner_breakdown
    )))

    # monthly
    p_m = sub.add_parser("monthly", help="Build a digest for a calendar month (or month-to-date)")
    p_m.add_argument("--logs-dir", required=True)
    p_m.add_argument("--output", "-o", required=True)
    p_m.add_argument("--group-actions", action="store_true")
    p_m.add_argument("--emit-kpis", action="store_true")
    p_m.add_argument("--owner-breakdown", action="store_true")
    def _month_bounds(today: dt.date) -> Tuple[dt.date, dt.date]:
        start = today.replace(day=1)
        return start, today
    p_m.set_defaults(_fn=lambda a: print(render_range(
        Path(a.logs_dir), *_month_bounds(dt.date.today()),
        f"Team Digest ({dt.date.today().replace(day=1)} - {dt.date.today()})",
        group_actions=a.group_actions, emit_kpis=a.emit_kpis, owner_breakdown=a.owner_breakdown
    )))

    # NEW: init
    p_i = sub.add_parser("init", help="Scaffold example logs/configs into a folder")
    p_i.add_argument("--to", default=".", help="Destination folder (default: current dir)")
    p_i.add_argument("--force", action="store_true", help="Overwrite existing files")
    p_i.set_defaults(_fn=cmd_init)

    # NEW: doctor
    p_x = sub.add_parser("doctor", help="Validate logs folder and show a quick summary")
    p_x.add_argument("--logs-dir", default="logs", help="Logs directory (default: logs)")
    p_x.set_defaults(_fn=cmd_doctor)

    args = parser.parse_args(argv)

    # Dispatch
    buf = args._fn(args)
    # Renderers return string (print already handled in lambdas above); commands return exit code
    if isinstance(buf, str):
        out = Path(getattr(args, "output", "-"))
        if str(out) == "-" or not out:
            sys.stdout.write(buf)
        else:
            Path(out).parent.mkdir(parents=True, exist_ok=True)
            out.write_text(buf, encoding="utf-8")
    elif isinstance(buf, int):
        return buf

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
