#!/usr/bin/env python3
import csv
import sys
from pathlib import Path

BASE = Path(__file__).resolve().parent
MANIFEST = BASE / "company_source_manifest.csv"
METRIC_MAP = BASE / "company_metric_map.yaml"

TARGETS = {
    "ORCL": "Oracle",
    "MSFT": "Microsoft",
    "AMZN": "Amazon",
    "GOOGL": "Alphabet",
    "META": "Meta Platforms",
    "CRWV": "CoreWeave",
    "DLR": "Digital Realty",
    "EQIX": "Equinix",
    "IRM": "Iron Mountain",
    "APLD": "Applied Digital",
    "CORZ": "Core Scientific",
    "IREN": "IREN",
    "WULF": "TeraWulf",
    "CIFR": "Cipher Mining",
    "HUT": "Hut 8",
    "NBIS": "Nebius",
    "NVDA": "NVIDIA",
    "VRT": "Vertiv",
    "ETN": "Eaton",
}

REQUIRED_COLUMNS = [
    "company",
    "ticker",
    "source_role",
    "source_family",
    "source_url",
    "stable_location",
    "extractable_metrics",
    "cadence",
    "basis_caveats",
    "extraction_difficulty",
    "notes",
]

ALLOWED_ROLES = {"primary", "secondary_discovery"}
ALLOWED_DIFFICULTY = {"low", "medium", "high"}
REQUIRED_METRICS = {
    "live_capacity",
    "capacity_added",
    "construction_underway",
    "future_capacity_pipeline",
    "secured_power",
    "lease_commitments",
    "cloud_capacity_commitments",
    "capex_actuals",
    "capex_guidance",
    "rpo_backlog",
    "revenue_conversion",
    "preleased_percentage",
    "expected_commencement_window",
    "equipment_orders",
    "book_to_bill",
    "data_center_commentary",
    "source_discovery",
}


def fail(errors, message):
    errors.append(message)


def main():
    errors = []
    if not MANIFEST.exists():
        fail(errors, f"missing {MANIFEST.name}")
    if not METRIC_MAP.exists():
        fail(errors, f"missing {METRIC_MAP.name}")
    if errors:
        return report(errors)

    metric_text = METRIC_MAP.read_text(encoding="utf-8")
    for metric in REQUIRED_METRICS:
        if f"  {metric}:" not in metric_text:
            fail(errors, f"metric map missing {metric}")

    with MANIFEST.open(newline="", encoding="utf-8") as f:
        rows = list(csv.DictReader(f))

    if not rows:
        fail(errors, "manifest has no rows")
        return report(errors)
    missing_columns = set(REQUIRED_COLUMNS) - set(rows[0])
    if missing_columns:
        fail(errors, f"missing columns: {sorted(missing_columns)}")

    primary_by_ticker = {ticker: 0 for ticker in TARGETS}
    metrics_seen = set()
    secondary_count = 0
    for i, row in enumerate(rows, start=2):
        for col in REQUIRED_COLUMNS:
            if not row.get(col, "").strip():
                fail(errors, f"row {i}: missing {col}")
        ticker = row.get("ticker", "")
        role = row.get("source_role", "")
        if ticker != "ALL" and ticker not in TARGETS:
            fail(errors, f"row {i}: unexpected ticker {ticker}")
        if role not in ALLOWED_ROLES:
            fail(errors, f"row {i}: invalid source_role {role}")
        if row.get("extraction_difficulty") not in ALLOWED_DIFFICULTY:
            fail(errors, f"row {i}: invalid extraction_difficulty {row.get('extraction_difficulty')}")
        if not row.get("source_url", "").startswith("https://"):
            fail(errors, f"row {i}: source_url must be https")
        metrics = [m.strip() for m in row.get("extractable_metrics", "").split(";") if m.strip()]
        if not metrics:
            fail(errors, f"row {i}: no extractable metrics")
        for metric in metrics:
            if metric != "source_discovery" and metric not in REQUIRED_METRICS:
                fail(errors, f"row {i}: metric {metric} missing from metric map")
            metrics_seen.add(metric)
        if role == "primary" and ticker in primary_by_ticker:
            primary_by_ticker[ticker] += 1
        if role == "secondary_discovery":
            secondary_count += 1
            if "secondary" not in row.get("notes", "").lower():
                fail(errors, f"row {i}: secondary row must be labeled secondary in notes")

    missing_primary = [ticker for ticker, count in primary_by_ticker.items() if count == 0]
    if missing_primary:
        fail(errors, f"missing primary source rows for: {missing_primary}")
    if secondary_count == 0:
        fail(errors, "expected at least one secondary_discovery row")
    if len(rows) < 38:
        fail(errors, f"expected at least 38 manifest rows, got {len(rows)}")

    return report(errors, rows=len(rows), tickers=len(primary_by_ticker), metrics=len(metrics_seen))


def report(errors, rows=0, tickers=0, metrics=0):
    if errors:
        print("Company source manifest validation failed")
        for error in errors:
            print(f"- {error}")
        return 1
    print("Company source manifest validation passed")
    print(f"- rows checked: {rows}")
    print(f"- target companies covered: {tickers}")
    print(f"- metric labels seen: {metrics}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
