from __future__ import annotations

from pathlib import Path
import sys
import re

import pandas as pd

from ledgerloom.artifacts import write_text
from ledgerloom.project.config import ProjectConfig


def _yaml_quote(value: str) -> str:
    """Return a YAML-safe single-quoted scalar."""
    return "'" + value.replace("'", "''") + "'"


def _default_check_outdir(project_root: Path, cfg: ProjectConfig) -> Path:
    # Keep consistent with ledgerloom.project.check._default_outdir (no private import).
    return project_root / cfg.outputs.root / "check" / cfg.project.period


def _render_rules_yaml(rows: pd.DataFrame, *, source_path: Path) -> str:
    """Render a copy/paste-friendly YAML snippet under `rules:`."""
    header = [
        f"# Generated by `ledgerloom suggest-mappings` from {source_path}",
        "# Paste these under: sources: - ... rules:",
        "# Then replace account: REPLACE_ME with a real chart-of-accounts code.",
    ]

    if rows.empty:
        return "\n".join(header + ["# No unmapped rows found; nothing to suggest.", "rules: []", ""])  # newline

    # Prefer suggested_pattern when present (added in PR08c).
    if "suggested_pattern" in rows.columns:
        patterns = rows["suggested_pattern"].fillna("").astype(str)
    elif "suggested_rule_yaml" in rows.columns:
        # Fallback: try to extract pattern: '...'
        pat = rows["suggested_rule_yaml"].fillna("").astype(str)
        patterns = pat.str.extract(r"pattern:\s*'([^']*)'", expand=False).fillna("")
    else:
        patterns = pd.Series([""] * len(rows))

    df = rows.copy()
    df["_pattern"] = patterns

    if "original_description" not in df.columns:
        df["original_description"] = ""

    df = df[df["_pattern"].str.strip().astype(bool)]
    if df.empty:
        return "\n".join(
            header
            + [
                "# No suggested_pattern values were found in unmapped.csv.",
                "# (Did you run a LedgerLoom version that writes suggested_pattern?)",
                "rules: []",
                "",
            ]
        )

    grouped = (
        df.groupby("_pattern", sort=True)
        .agg(
            count=("_pattern", "size"),
            example_desc=("original_description", lambda s: next((x for x in s if str(x).strip()), "")),
        )
        .reset_index()
        .rename(columns={"_pattern": "pattern"})
    )

    lines: list[str] = []
    lines.extend(header)
    lines.append("rules:")

    for _, r in grouped.iterrows():
        pattern = str(r["pattern"])
        count = int(r["count"])
        example = str(r.get("example_desc", "")).strip()
        example = re.sub(r"\s+", " ", example)
        if len(example) > 60:
            example = example[:57] + "..."

        lines.append(f"  - pattern: {_yaml_quote(pattern)}")
        comment = f"# {count} row(s)"
        if example:
            comment += f"; e.g. {example}"
        lines.append(f"    account: {_yaml_quote('REPLACE_ME')}  {comment}")

    lines.append("")
    return "\n".join(lines)


def run_suggest_mappings(
    *,
    project_root: Path,
    config_path: str = "ledgerloom.yaml",
    outdir: str | None = None,
    unmapped_path: str | None = None,
    out_path: str | None = None,
) -> int:
    """CLI entrypoint: read unmapped.csv, dedupe, print YAML rules."""
    project_root = project_root.resolve()

    if unmapped_path:
        unmapped_csv = Path(unmapped_path).expanduser()
    else:
        cfg_file = Path(config_path)
        if not cfg_file.is_absolute():
            cfg_file = project_root / cfg_file
        cfg = ProjectConfig.load_yaml(cfg_file)

        check_outdir = Path(outdir).expanduser() if outdir else _default_check_outdir(project_root, cfg)
        unmapped_csv = check_outdir / "unmapped.csv"

    if not unmapped_csv.exists():
        print(
            f"ERROR: unmapped.csv not found at: {unmapped_csv}\n"
            "Run: ledgerloom check --project <dir> (or pass --unmapped /path/to/unmapped.csv)",
            file=sys.stderr,
        )
        return 2

    df = pd.read_csv(unmapped_csv, keep_default_na=False)
    yaml_text = _render_rules_yaml(df, source_path=unmapped_csv)

    if out_path:
        out_file = Path(out_path).expanduser()
        write_text(out_file, yaml_text)
        print(f"Wrote mapping suggestions -> {out_file}")
    else:
        print(yaml_text, end="")

    return 0
