import argparse
import json
from pathlib import Path

from request_report import (
    build_report,
    resolve_privacy_options,
    resolve_requests_path,
    resolve_staleness_options,
)
from state_consistency_check import build_consistency_report


CONFIG_PATH = ".prd_plugin/config.json"
REQUESTS_PATH = ".prd_plugin/state/requests.json"
SCRIPT_SCOPE_PATH = ".prd_plugin/templates/script-install-scope.json"
DEFAULT_REQUIRED_SKILLS = [
    "project-memory",
    "project-session-close",
    "project-traceability-sync",
    "project-request-intake",
]
PLUGIN_DEVELOPMENT_SKILLS = {"project-fold-it-in"}
PROHIBITED_DOWNSTREAM_SCRIPTS = {
    "gap_audit.py",
    "local_workflow_check.py",
    "prd_install.py",
    "release_check.py",
    "request_import.py",
    "request_mailbox.py",
    "version_advice.py",
}


def _read_json(path):
    return json.loads(Path(path).read_text(encoding="utf-8-sig"))


def _finding(identifier, severity, summary, required_action, path=None):
    finding = {
        "id": identifier,
        "severity": severity,
        "summary": summary,
        "required_action": required_action,
    }
    if path:
        finding["path"] = str(path)
    return finding


def _config_path(root):
    return Path(root) / CONFIG_PATH


def _requests_path(root, config_path):
    return resolve_requests_path(None, Path(root) / REQUESTS_PATH, config_path)


def _check_config(root):
    path = _config_path(root)
    if not path.exists():
        return None, [
            _finding(
                "DOC-ERR-001",
                "error",
                "PRD Plugin config is missing.",
                "Create .prd_plugin/config.json from the PRD Plugin template before relying on project state.",
                CONFIG_PATH,
            )
        ]

    try:
        config = _read_json(path)
    except json.JSONDecodeError as exc:
        return None, [
            _finding(
                "DOC-ERR-001",
                "error",
                "PRD Plugin config is not valid JSON.",
                f"Fix JSON syntax in {CONFIG_PATH} at line {exc.lineno}, column {exc.colno}.",
                CONFIG_PATH,
            )
        ]

    findings = [
        _finding(
            "DOC-INFO-001",
            "info",
            "PRD Plugin config is present and parseable.",
            "No action required.",
            CONFIG_PATH,
        )
    ]
    installed_version = config.get("plugin", {}).get("installed_version")
    if installed_version:
        findings.append(
            _finding(
                "DOC-INFO-002",
                "info",
                f"Installed PRD Plugin version marker is {installed_version}.",
                "Run version advice from the plugin hub before bumping this downstream repo.",
                CONFIG_PATH,
            )
        )
    return config, findings


def _check_requests(root, config_path):
    path = _requests_path(root, config_path)
    if not Path(path).exists():
        return []

    try:
        privacy_options = resolve_privacy_options(config_path)
        staleness_options = resolve_staleness_options(config_path)
        report = build_report(path, **privacy_options, **staleness_options)
    except (json.JSONDecodeError, ValueError) as exc:
        return [
            _finding(
                "DOC-ERR-002",
                "error",
                "Request state could not be analyzed.",
                f"Fix {Path(path).as_posix()}: {exc}",
                path,
            )
        ]

    findings = []
    if report["needs_attention"]:
        findings.append(
            _finding(
                "DOC-WARN-001",
                "warning",
                f"{len(report['needs_attention'])} request issue(s) need attention.",
                "Run request triage or update the affected REQ-* threads.",
                path,
            )
        )
    if report["privacy_warnings"]:
        findings.append(
            _finding(
                "DOC-WARN-002",
                "warning",
                f"{len(report['privacy_warnings'])} request privacy warning(s) were found.",
                "Sanitize request scope, visibility, and local-state references before upstream export.",
                path,
            )
        )
    return findings


def _check_script_scope(root, config=None):
    root = Path(root)
    findings = []
    repo_scope = ""
    if isinstance(config, dict):
        repo_scope = str(config.get("privacy", {}).get("repo_scope", "")).strip().lower()
    scripts_dir = root / "scripts"
    if scripts_dir.exists() and repo_scope != "upstream":
        copied = sorted(
            path.name
            for path in scripts_dir.glob("*.py")
            if path.name in PROHIBITED_DOWNSTREAM_SCRIPTS
        )
        if copied:
            findings.append(
                _finding(
                    "DOC-WARN-004",
                    "warning",
                    "Repo contains PRD Plugin hub/development scripts that should not be blanket-installed downstream.",
                    f"Remove or justify these scripts according to {SCRIPT_SCOPE_PATH}: {', '.join(copied)}.",
                    "scripts",
                )
            )
    return findings


def _check_repo_local_skills(root, config=None):
    if not isinstance(config, dict):
        return []
    skills_config = config.get("skills", {})
    if not isinstance(skills_config, dict) or not skills_config.get("install_repo_local_by_default"):
        return []

    repo_local_path = skills_config.get("repo_local_path", ".agents/skills")
    required_skills = skills_config.get("required_skills") or DEFAULT_REQUIRED_SKILLS
    skills_root = Path(root) / repo_local_path
    missing = [
        skill_name
        for skill_name in required_skills
        if not (skills_root / skill_name / "SKILL.md").is_file()
    ]
    findings = []
    installed_plugin_development = sorted(
        skill_name
        for skill_name in PLUGIN_DEVELOPMENT_SKILLS
        if (skills_root / skill_name / "SKILL.md").is_file()
    )
    if installed_plugin_development:
        findings.append(
            _finding(
                "DOC-WARN-006",
                "warning",
                "Repo-local skill install contains PRD Plugin development workflows.",
                "Run `python scripts/prd_install_skills.py --repo-root .` to refresh downstream_runtime skills and prune plugin-development skills.",
                repo_local_path,
            )
        )
    if not missing:
        findings.append(
            _finding(
                "DOC-INFO-003",
                "info",
                "Repo-local PRD Plugin skills are installed for Codex discovery.",
                "No action required.",
                repo_local_path,
            )
        )
    else:
        findings.append(
            _finding(
                "DOC-WARN-005",
                "warning",
                "Repo-local PRD Plugin skills are missing or incomplete.",
                "Run `python scripts/prd_install_skills.py --repo-root .` from the PRD Plugin hub/plugin bundle or install the skeleton `.agents/skills` directory.",
                repo_local_path,
            )
        )

    opencode_target = skills_config.get("opencode_target", ".opencode/skill")
    if bool(skills_config.get("install_opencode_by_default")):
        opencode_root = Path(root) / opencode_target
        opencode_missing = [
            skill_name
            for skill_name in required_skills
            if not (opencode_root / skill_name / "SKILL.md").is_file()
        ]
        if not opencode_missing:
            findings.append(
                _finding(
                    "DOC-INFO-004",
                    "info",
                    "Repo-local PRD Plugin skills are installed for opencode discovery.",
                    "No action required.",
                    opencode_target,
                )
            )
        else:
            findings.append(
                _finding(
                    "DOC-WARN-007",
                    "warning",
                    "Repo-local PRD Plugin skills are missing for opencode discovery.",
                    f"Run `python scripts/prd_install_skills.py --repo-root . --target-agent opencode` from the PRD Plugin hub/plugin bundle or install the skeleton `{opencode_target}` directory.",
                    opencode_target,
                )
            )

    claude_target = skills_config.get("claude_target", ".claude/skills")
    if bool(skills_config.get("install_claude_by_default")):
        claude_root = Path(root) / claude_target
        claude_missing = [
            skill_name
            for skill_name in required_skills
            if not (claude_root / skill_name / "SKILL.md").is_file()
        ]
        if not claude_missing:
            findings.append(
                _finding(
                    "DOC-INFO-005",
                    "info",
                    "Repo-local PRD Plugin skills are installed for Claude Code discovery.",
                    "No action required.",
                    claude_target,
                )
            )
        else:
            findings.append(
                _finding(
                    "DOC-WARN-009",
                    "warning",
                    "Repo-local PRD Plugin skills are missing for Claude Code discovery.",
                    f"Run `python scripts/prd_install_skills.py --repo-root . --target-agent claude` from the PRD Plugin hub/plugin bundle or install the skeleton `{claude_target}` directory.",
                    claude_target,
                )
            )
    return findings


def _check_state_consistency(root):
    try:
        report = build_consistency_report(root)
    except (json.JSONDecodeError, ValueError) as exc:
        return [
            _finding(
                "DOC-ERR-003",
                "error",
                "State consistency could not be analyzed.",
                f"Fix PRD Plugin state consistency inputs: {exc}",
                ".prd_plugin/state",
            )
        ]

    if report["status"] != "error":
        return []

    return [
        _finding(
            "DOC-ERR-003",
            "error",
            f"{report['summary']['errors']} state consistency issue(s) were found.",
            "Run `python scripts/state_consistency_check.py --repo-root .` and fix missing claimed IDs, registry drift, or future timestamps before claiming completion.",
            ".prd_plugin/state",
        )
    ]


def _status_for(findings):
    if any(finding["severity"] == "error" for finding in findings):
        return "error"
    if any(finding["severity"] == "warning" for finding in findings):
        return "warning"
    return "ok"


def _summary_for(findings):
    return {
        "errors": sum(1 for finding in findings if finding["severity"] == "error"),
        "warnings": sum(1 for finding in findings if finding["severity"] == "warning"),
        "info": sum(1 for finding in findings if finding["severity"] == "info"),
    }


def _check_plugin_manifests(root):
    findings = []
    for rel_path in (
        ".codex-plugin/plugin.json",
        ".opencode/plugin.json",
        ".claude-plugin/plugin.json",
    ):
        path = Path(root) / rel_path
        if not path.is_file():
            findings.append(
                _finding(
                    "DOC-WARN-008",
                    "warning",
                    f"{rel_path} is missing.",
                    "Re-run the PRD Plugin installer from the hub to restore host-agent plugin manifests.",
                    rel_path,
                )
            )
            continue
        try:
            data = _read_json(path)
            version = data.get("version")
            if not version:
                findings.append(
                    _finding(
                        "DOC-WARN-009",
                        "warning",
                        f"{rel_path} does not declare a plugin version.",
                        "Re-run the PRD Plugin installer from the hub or add a version field.",
                        rel_path,
                    )
                )
        except json.JSONDecodeError as exc:
            findings.append(
                _finding(
                    "DOC-ERR-004",
                    "error",
                    f"{rel_path} is not valid JSON.",
                    f"Fix JSON syntax at line {exc.lineno}, column {exc.colno}.",
                    rel_path,
                )
            )
    return findings


def build_doctor_report(repo_root="."):
    root = Path(repo_root).resolve()
    config, findings = _check_config(root)
    config_path = _config_path(root)

    if config is not None:
        findings.extend(_check_requests(root, config_path))
    else:
        requests_path = root / REQUESTS_PATH
        if requests_path.exists():
            try:
                _read_json(requests_path)
            except json.JSONDecodeError as exc:
                findings.append(
                    _finding(
                        "DOC-ERR-002",
                        "error",
                        "Request state is not valid JSON.",
                        f"Fix {REQUESTS_PATH} at line {exc.lineno}, column {exc.colno}.",
                        REQUESTS_PATH,
                    )
                )

    findings.extend(_check_script_scope(root, config))
    findings.extend(_check_repo_local_skills(root, config))
    findings.extend(_check_plugin_manifests(root))
    if config is not None:
        findings.extend(_check_state_consistency(root))
    return {
        "status": _status_for(findings),
        "repo_root": str(root),
        "summary": _summary_for(findings),
        "findings": findings,
    }


def format_markdown(report):
    summary = report["summary"]
    lines = [
        "# PRD Plugin Doctor",
        "",
        f"Status: `{report['status']}`",
        f"Repo: `{report['repo_root']}`",
        "",
        "| Severity | Count |",
        "| --- | ---: |",
        f"| Errors | {summary['errors']} |",
        f"| Warnings | {summary['warnings']} |",
        f"| Info | {summary['info']} |",
        "",
        "## Findings",
        "",
    ]
    if not report["findings"]:
        lines.append("- None")
    else:
        for finding in report["findings"]:
            path = f" ({finding['path']})" if finding.get("path") else ""
            lines.append(
                f"- `{finding['id']}` `{finding['severity']}`{path}: {finding['summary']} "
                f"Required action: {finding['required_action']}"
            )
    return "\n".join(lines) + "\n"


def main(argv=None):
    parser = argparse.ArgumentParser(description="Run read-only PRD Plugin downstream repo diagnostics.")
    parser.add_argument("--repo-root", default=".", help="Repository root to inspect.")
    parser.add_argument("--format", choices=("markdown", "json"), default="markdown")
    parser.add_argument("--output", help="Optional output path.")
    args = parser.parse_args(argv)

    report = build_doctor_report(args.repo_root)
    if args.format == "json":
        content = json.dumps(report, indent=2) + "\n"
    else:
        content = format_markdown(report)

    if args.output:
        output_path = Path(args.output)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        output_path.write_text(content, encoding="utf-8")
    else:
        print(content, end="")

    return 1 if report["status"] == "error" else 0


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