"""코드 리뷰가 무엇을 읽고 결과 파일을 어디에 쓰는지 해소한다.

두 모드를 한 진입점에 모아 스킬이 base 커밋도, 리뷰 파일 경로도, 회차도 다시
유도하지 않게 한다. stage 모드는 read side 에 그대로 위임하고, branch 모드만
여기서 해소한다 — caller 가 base 를 지정하지 않으면 기본 브랜치와의
merge-base 를 쓴다. 이 모듈은 인자 검증과 JSON 정형화만 하고, 경로·회차 조립은
code_review_paths 가, stage 규칙은 code_review_target_snapshot 이 소유한다.
"""
from __future__ import annotations

import argparse
import json
import re
import subprocess
import sys
from datetime import date
from pathlib import Path

from okstra_project import (
    ResolverError,
    StateError,
    code_review_target_snapshot,
    resolve_project_root,
)

from .code_review_paths import branch_review_dir, next_branch_review
from .fixed_text import line

DEFAULT_BRANCH_CANDIDATES = ("main", "master")

_DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")


def _git_out(repo: Path, *args: str) -> str:
    result = subprocess.run(
        ["git", "-C", str(repo), *args],
        capture_output=True,
        text=True,
        check=False,
    )
    return result.stdout.strip() if result.returncode == 0 else ""


def _default_branch_ref(repo: Path) -> str:
    """merge-base 를 잴 기준 ref — origin/HEAD 가 있으면 그것, 없으면 관례 이름."""
    remote_head = _git_out(repo, "symbolic-ref", "--quiet", "refs/remotes/origin/HEAD")
    if remote_head:
        return remote_head
    for candidate in DEFAULT_BRANCH_CANDIDATES:
        if _git_out(repo, "rev-parse", "--verify", "--quiet", candidate):
            return candidate
    return ""


def _merge_base(repo: Path, branch: str) -> str:
    default_ref = _default_branch_ref(repo)
    merge_base = _git_out(repo, "merge-base", default_ref, branch) if default_ref else ""
    if not merge_base:
        raise StateError(
            f"no merge-base for {branch!r} against the default branch — "
            "pass --base explicitly",
            stage="base_unresolved",
        )
    return merge_base


def _review_date(raw: str) -> str:
    """branch 결과 파일명에 쓸 날짜.

    next_branch_review 는 넘겨받은 문자열을 그대로 파일명에 쓰지만 되읽을 때는
    `YYYY-MM-DD` 만 인식한다. 어긋난 날짜를 통과시키면 회차가 늘 1 로 돌아가
    직전 리뷰 파일을 덮어쓴다.
    """
    if not raw:
        return date.today().isoformat()
    if not _DATE_RE.match(raw):
        raise StateError(f"--date must be YYYY-MM-DD, got {raw!r}", stage="args")
    return raw


def _branch_snapshot(repo: Path, branch: str, base: str, review_date: str) -> dict:
    head = _git_out(repo, "rev-parse", branch)
    if not head:
        raise StateError(f"branch {branch!r} not found", stage="branch_missing")
    base_commit = base or _merge_base(repo, branch)
    review_path, seq = next_branch_review(branch_review_dir(repo, branch), review_date)
    return {
        "mode": "branch",
        "worktreePath": str(repo),
        "branch": branch,
        "baseCommit": base_commit,
        "headCommit": head,
        "reviewPath": str(review_path),
        "round": seq,
    }


def _build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="Resolve a code review's diff range and result file path."
    )
    parser.add_argument("--task-key", default="", help="project-id:task-group:task-id")
    parser.add_argument("--stage", type=int, default=0, help="stage number to review")
    parser.add_argument("--branch", default="", help="branch to review")
    parser.add_argument("--base", default="", help="explicit base ref for branch mode")
    parser.add_argument("--date", default="", help="YYYY-MM-DD for branch result naming")
    parser.add_argument("--project-root", default="", help="explicit project root")
    parser.add_argument("--cwd", default=".", help="cwd for project root resolution")
    output = parser.add_mutually_exclusive_group()
    output.add_argument("--json", action="store_true", help="emit JSON (default)")
    output.add_argument("--text", action="store_true", help="emit fixed text fields")
    return parser


def _resolve(args: argparse.Namespace, project_root: Path) -> dict:
    if args.task_key:
        if args.stage < 1:
            raise StateError("--task-key needs --stage <N>, N >= 1", stage="args")
        return code_review_target_snapshot(project_root, args.task_key, args.stage)
    if args.branch:
        return _branch_snapshot(
            project_root, args.branch, args.base, _review_date(args.date)
        )
    raise StateError("pass either --task-key --stage or --branch", stage="args")


def render_code_review_target_text(payload: dict) -> str:
    """모델이 소비할 리뷰 범위와 결과 경로만 고정 줄로 투영한다."""
    rows = ["Okstra code review target\n"]
    rows.append(line("Status", "ready" if payload.get("ok") else "error"))
    fields = (
        ("Project root", "projectRoot"), ("Mode", "mode"),
        ("Worktree path", "worktreePath"), ("Branch", "branch"),
        ("Base commit", "baseCommit"), ("Head commit", "headCommit"),
        ("Review path", "reviewPath"), ("Round", "round"),
        ("Task key", "taskKey"), ("Task root", "taskRoot"), ("Stage", "stage"),
        ("Failure stage", "stage"), ("Failure reason", "reason"),
    )
    for label, key in fields:
        failure_field = label.startswith("Failure")
        if key in payload and failure_field != bool(payload.get("ok")):
            rows.append(line(label, payload.get(key)))
    return "".join(rows)


def main(argv: list[str] | None = None) -> int:
    args = _build_parser().parse_args(argv)
    try:
        project_root = resolve_project_root(
            explicit_root=args.project_root, cwd=args.cwd
        )
    except ResolverError as exc:
        payload = {"ok": False, "stage": "resolve", "reason": str(exc)}
        print(render_code_review_target_text(payload) if args.text else json.dumps(payload))
        return 2

    try:
        snapshot = _resolve(args, Path(project_root))
    except StateError as exc:
        payload = {
            "ok": False,
            "stage": exc.stage or "target",
            "reason": str(exc),
        }
        print(render_code_review_target_text(payload) if args.text else json.dumps(payload))
        return 1

    payload = {"ok": True, "projectRoot": str(project_root), **snapshot}
    print(
        render_code_review_target_text(payload)
        if args.text
        else json.dumps(payload, ensure_ascii=False, indent=2)
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
