#!/usr/bin/env python3
"""Validate the structure and minimum gates of a product strategy project."""

from __future__ import annotations

import argparse
import csv
from pathlib import Path
import sys


REQUIRED_FILES = [
    "README.md",
    "ROUTING_NOTE.md",
    "TASK_CONTRACT.md",
    "OPERATING_MODEL.md",
    "FIRST_RUN.md",
    "source-index.csv",
    "summary.md",
    "data/source-index.csv",
    "data/product-map.csv",
    "data/customer-segments.csv",
    "data/competitor-map.csv",
    "data/evidence-gap-list.csv",
    "analysis/span-score.csv",
    "analysis/appeals-score.csv",
    "analysis/pdc-score.csv",
    "analysis/7-2-1-allocation.csv",
    "outputs/decision-log.csv",
    "outputs/product-roadmap.csv",
    "outputs/kpi-dashboard.csv",
]

CSV_HEADERS = {
    "data/source-index.csv": [
        "source_id", "title", "source_type", "location", "evidence_level",
        "status", "owner", "updated_at", "notes",
    ],
    "data/product-map.csv": [
        "product_line", "product_or_family", "target_customer", "region",
        "channel", "revenue", "gross_margin", "growth_rate", "owner",
        "source_ids", "confidence", "status",
    ],
    "data/customer-segments.csv": [
        "segment_id", "segment_name", "customer_profile", "core_need",
        "current_alternative", "market_size", "growth", "accessibility",
        "profitability", "stability", "source_ids", "decision",
    ],
    "data/competitor-map.csv": [
        "competitor", "target_segment", "offering", "price", "channel",
        "strengths", "weaknesses", "delivery_model", "source_ids", "confidence",
    ],
    "data/evidence-gap-list.csv": [
        "gap_id", "decision_blocked", "missing_data", "owner", "due_date",
        "next_safe_action", "status",
    ],
    "analysis/span-score.csv": [
        "object", "market_attractiveness", "competitive_position", "weight_set",
        "quadrant", "recommended_action", "source_ids", "confidence", "reviewer",
    ],
    "analysis/appeals-score.csv": [
        "target_segment", "dimension", "weight", "our_score", "competitor",
        "competitor_score", "gap", "action", "source_ids", "confidence",
    ],
    "analysis/pdc-score.csv": [
        "object", "market_score", "competitive_score", "financial_score",
        "repeatability_score", "weighted_total", "rank", "source_ids",
        "confidence", "reviewer",
    ],
    "analysis/7-2-1-allocation.csv": [
        "object", "category", "reason", "resource_action", "owner", "metric",
        "review_date", "exit_condition", "source_ids", "approval_status",
    ],
    "outputs/decision-log.csv": [
        "decision_id", "decision_date", "question", "decision", "alternatives",
        "owner", "reviewers", "source_ids", "assumptions", "next_review_date",
    ],
    "outputs/product-roadmap.csv": [
        "product", "stage", "target_customer", "customer_need", "milestone",
        "success_metric", "owner", "due_date", "source_ids", "decision_id",
    ],
    "outputs/kpi-dashboard.csv": [
        "metric_id", "metric", "definition", "baseline", "target", "period",
        "owner", "data_source", "refresh_frequency", "status",
    ],
}

STRICT_MARKERS = {
    "TASK_CONTRACT.md": [
        "待产品负责人确认",
        "待确认",
    ],
}


def read_csv(path: Path) -> tuple[list[str], list[dict[str, str]]]:
    with path.open("r", encoding="utf-8-sig", newline="") as handle:
        reader = csv.DictReader(handle)
        return reader.fieldnames or [], list(reader)


def validate(root: Path, strict: bool) -> list[str]:
    errors: list[str] = []

    for relative in REQUIRED_FILES:
        if not (root / relative).is_file():
            errors.append(f"missing file: {relative}")

    for relative, expected in CSV_HEADERS.items():
        path = root / relative
        if not path.is_file():
            continue
        actual, _ = read_csv(path)
        if actual != expected:
            errors.append(
                f"invalid CSV header: {relative}\n"
                f"  expected: {','.join(expected)}\n"
                f"  actual:   {','.join(actual)}"
            )

    if strict:
        for relative, markers in STRICT_MARKERS.items():
            path = root / relative
            if not path.is_file():
                continue
            content = path.read_text(encoding="utf-8")
            for marker in markers:
                if marker in content:
                    errors.append(f"strict gate unresolved in {relative}: {marker}")

        source_path = root / "data/source-index.csv"
        if source_path.is_file():
            _, sources = read_csv(source_path)
            usable_sources = [
                row
                for row in sources
                if row.get("evidence_level") in {"A", "B"}
                and row.get("status") in {"available", "verified"}
                and row.get("location", "").strip()
            ]
            if not usable_sources:
                errors.append(
                    "strict gate: at least one available/verified A/B-level source "
                    "with a recoverable location is required"
                )

        allocation_path = root / "analysis/7-2-1-allocation.csv"
        if allocation_path.is_file():
            _, actions = read_csv(allocation_path)
            if not actions:
                errors.append("strict gate: at least one reviewed 7-2-1 action is required")
            required_action_fields = ["owner", "metric", "review_date", "exit_condition"]
            for index, row in enumerate(actions, start=2):
                for field in required_action_fields:
                    if not row.get(field, "").strip():
                        errors.append(
                            f"strict gate: analysis/7-2-1-allocation.csv row {index} "
                            f"is missing {field}"
                        )

    return errors


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("project_root", type=Path)
    parser.add_argument("--strict", action="store_true")
    args = parser.parse_args()

    root = args.project_root.resolve()
    if not root.is_dir():
        print(f"ERROR: project root does not exist: {root}", file=sys.stderr)
        return 2

    errors = validate(root, args.strict)
    if errors:
        print("VALIDATION FAILED")
        for error in errors:
            print(f"- {error}")
        return 1

    mode = "strict" if args.strict else "bootstrap"
    print(f"VALIDATION PASSED ({mode}): {root}")
    return 0


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