#!/usr/bin/env python3
"""
Manifest-driven APLD forecast-vs-actual ledger extractor.

The extractor intentionally uses primary-source manifest entries plus compact
source fragments and regex extraction rules. It does not scrape prose summaries.
Run from the artifacts directory:

    python extract_apld_forecast_actual.py

Outputs:
    apld_forecast_actual_rows.csv
"""

from __future__ import annotations

import csv
import re
from decimal import Decimal
from pathlib import Path
from typing import Dict, Iterable, List


OUT = Path(__file__).resolve().parent
MANIFEST_PATH = OUT / "apld_forecast_actual_manifest.csv"
ROWS_PATH = OUT / "apld_forecast_actual_rows.csv"

FIELDS = [
    "company",
    "period",
    "metric_name",
    "raw_value",
    "normalized_value",
    "unit",
    "value_qualifier",
    "ownership_basis",
    "forecast_or_actual",
    "is_derived",
    "source_url",
    "source_location",
    "source_snippet",
    "snippet_support_status",
    "confidence",
    "caveat",
]

ROW_SPECS = [
    {
        "source_id": "APLD_2025_10K",
        "company": "APLD",
        "period": "FY2025",
        "metric_name": "capex_actual_total",
        "raw_value": "$985.826 million",
        "unit": "USD_millions",
        "value_qualifier": "exact",
        "ownership_basis": "consolidated",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-K Note 16 segment disclosures, lines 3889-3901",
        "source_snippet": "Total capital expenditures $985,826",
        "extract_regex": r"Total capital expenditures\s*\$?([0-9,]+)",
        "scale": "0.001",
        "confidence": "high",
        "caveat": "SEC table is in thousands; normalized to USD millions.",
    },
    {
        "source_id": "APLD_2025_10K",
        "company": "APLD",
        "period": "FY2025",
        "metric_name": "capex_actual_hpc_hosting",
        "raw_value": "$976.506 million",
        "unit": "USD_millions",
        "value_qualifier": "exact",
        "ownership_basis": "HPC Hosting segment",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-K Note 16 segment disclosures, lines 3889-3898",
        "source_snippet": "HPC Hosting Business 976,506",
        "extract_regex": r"HPC Hosting Business\s*\$?([0-9,]+)",
        "scale": "0.001",
        "confidence": "high",
        "caveat": "SEC table is in thousands; segment capex includes HPC Hosting Business.",
    },
    {
        "source_id": "APLD_2025_10K",
        "company": "APLD",
        "period": "FY2025",
        "metric_name": "construction_in_progress",
        "raw_value": "$1.123156 billion",
        "unit": "USD_millions",
        "value_qualifier": "exact",
        "ownership_basis": "consolidated property and equipment",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-K Note 3 property and equipment, lines 2673-2703",
        "source_snippet": "Construction in progress 1,123,156",
        "extract_regex": r"Construction in progress\s*\$?([0-9,]+)",
        "scale": "0.001",
        "confidence": "high",
        "caveat": "SEC table is in thousands; includes assets received but not placed in service.",
    },
    {
        "source_id": "APLD_2026Q1_10Q",
        "company": "APLD",
        "period": "FY2026Q1",
        "metric_name": "capex_actual_purchases_property_equipment_other_assets",
        "raw_value": "$249.420 million",
        "unit": "USD_millions",
        "value_qualifier": "exact",
        "ownership_basis": "consolidated cash-flow statement",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q cash flows, lines 429-430",
        "source_snippet": "Purchases ... property and equipment ... (249,420)",
        "extract_regex": r"\(([0-9,]+)\)",
        "scale": "0.001",
        "confidence": "high",
        "caveat": "Cash-flow line includes other assets and is presented in thousands.",
    },
    {
        "source_id": "APLD_2026Q1_10Q",
        "company": "APLD",
        "period": "FY2026Q1",
        "metric_name": "construction_in_progress",
        "raw_value": "$1.309756 billion",
        "unit": "USD_millions",
        "value_qualifier": "exact",
        "ownership_basis": "consolidated property and equipment",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q Note 3 property and equipment, lines 607-636",
        "source_snippet": "Construction in progress 1,309,756",
        "extract_regex": r"Construction in progress\s*\$?([0-9,]+)",
        "scale": "0.001",
        "confidence": "high",
        "caveat": "SEC table is in thousands.",
    },
    {
        "source_id": "APLD_2026Q1_10Q",
        "company": "APLD",
        "period": "FY2026Q1",
        "metric_name": "live_capacity_data_center_hosting",
        "raw_value": "106 MW Jamestown + 180 MW Ellendale",
        "unit": "MW",
        "value_qualifier": "exact_sum",
        "ownership_basis": "operated data-center hosting facilities",
        "forecast_or_actual": "actual",
        "is_derived": "true",
        "source_location": "10-Q business update, lines 1593-1598",
        "source_snippet": "106 MW ... 180 MW ... full capacity",
        "extract_regex": r"([0-9]+)\s*MW.*?([0-9]+)\s*MW",
        "derive": "sum_groups",
        "confidence": "high",
        "caveat": "Derived by summing two operated crypto data-center hosting facilities.",
    },
    {
        "source_id": "APLD_2026Q1_10Q",
        "company": "APLD",
        "period": "FY2026Q1",
        "metric_name": "contracted_capacity_polaris_forge_1",
        "raw_value": "400 MW",
        "unit": "MW",
        "value_qualifier": "exact",
        "ownership_basis": "APLD leased critical IT load",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q leases/business update, lines 1148-1164 and 1601-1606",
        "source_snippet": "total capacity under contract ... 400 MW",
        "extract_regex": r"([0-9]+)\s*MW",
        "confidence": "high",
        "caveat": "Represents contracted critical IT capacity, not fully energized capacity.",
    },
    {
        "source_id": "APLD_2026Q1_10Q",
        "company": "APLD",
        "period": "FY2026Q1",
        "metric_name": "minimum_contracted_lease_payments",
        "raw_value": "$10.851517 billion",
        "unit": "USD_millions",
        "value_qualifier": "exact",
        "ownership_basis": "lessor minimum contracted payments",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q Note 11 leases, lines 1164-1172",
        "source_snippet": "Total $10,851,517",
        "extract_regex": r"Total\s*\$?([0-9,]+)",
        "scale": "0.001",
        "confidence": "high",
        "caveat": "SEC table is in thousands and excludes operating-expense reimbursements and variable rent increases.",
    },
    {
        "source_id": "APLD_2025_06_02_PR",
        "company": "APLD",
        "period": "CY2025-06-02",
        "metric_name": "contracted_capacity_coreweave_initial",
        "raw_value": "250 MW",
        "unit": "MW",
        "value_qualifier": "up_to",
        "ownership_basis": "APLD leased critical IT load",
        "forecast_or_actual": "forecast",
        "is_derived": "false",
        "source_location": "IR press release PDF, page 1 lines 4-15",
        "source_snippet": "deliver 250 megawatts",
        "extract_regex": r"([0-9]+)\s*megawatts",
        "confidence": "high",
        "caveat": "Forward-looking lease delivery statement at announcement date.",
    },
    {
        "source_id": "APLD_2025_06_02_PR",
        "company": "APLD",
        "period": "CY2025-06-02",
        "metric_name": "contracted_revenue_coreweave_initial",
        "raw_value": "approximately $7 billion",
        "unit": "USD_millions",
        "value_qualifier": "approximately",
        "ownership_basis": "APLD anticipated lease revenue",
        "forecast_or_actual": "forecast",
        "is_derived": "false",
        "source_location": "IR press release PDF, page 1 lines 8-12",
        "source_snippet": "approximately $7 billion",
        "extract_regex": r"\$([0-9]+)\s*billion",
        "scale": "1000",
        "confidence": "high",
        "caveat": "Management anticipated total revenue over approximately 15-year lease terms.",
    },
    {
        "source_id": "APLD_2025_06_02_PR",
        "company": "APLD",
        "period": "CY2025-06-02",
        "metric_name": "expected_commencement_window_building_2",
        "raw_value": "ready for service in fourth quarter calendar 2025",
        "normalized_value": "2025-Q4",
        "unit": "calendar_quarter",
        "value_qualifier": "expected",
        "ownership_basis": "APLD leased facility schedule",
        "forecast_or_actual": "forecast",
        "is_derived": "false",
        "source_location": "IR press release PDF, page 2 lines 35-36",
        "source_snippet": "100 MW ... fourth quarter ... 2025",
        "extract_regex": r"100\s*MW.*fourth quarter.*2025",
        "confidence": "high",
        "caveat": "Forward-looking ready-for-service timing.",
    },
    {
        "source_id": "APLD_2025_06_02_PR",
        "company": "APLD",
        "period": "CY2025-06-02",
        "metric_name": "expected_commencement_window_building_3",
        "raw_value": "150 MW data center ready for service in middle of 2026",
        "normalized_value": "2026-mid",
        "unit": "calendar_window",
        "value_qualifier": "expected",
        "ownership_basis": "APLD leased facility schedule",
        "forecast_or_actual": "forecast",
        "is_derived": "false",
        "source_location": "IR press release PDF, page 2 lines 36-38",
        "source_snippet": "150 MW ... middle of 2026",
        "extract_regex": r"150\s*MW.*middle of 2026",
        "confidence": "high",
        "caveat": "Forward-looking schedule; not a precise quarter.",
    },
    {
        "source_id": "APLD_2025_08_29_8K",
        "company": "APLD",
        "period": "CY2025-08-29",
        "metric_name": "contracted_capacity_building_4",
        "raw_value": "150 MW",
        "unit": "MW",
        "value_qualifier": "exact",
        "ownership_basis": "APLD leased critical IT load",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "8-K Item 1.01, lines 54-58",
        "source_snippet": "Building 4 ... 150MW",
        "extract_regex": r"([0-9]+)\s*MW",
        "confidence": "high",
        "caveat": "Actual executed lease; capacity delivery remains future construction.",
    },
    {
        "source_id": "APLD_2025_08_29_8K",
        "company": "APLD",
        "period": "CY2025-08-29",
        "metric_name": "contracted_revenue_building_4",
        "raw_value": "approximately $4.0 billion",
        "unit": "USD_millions",
        "value_qualifier": "approximately",
        "ownership_basis": "APLD anticipated lease revenue",
        "forecast_or_actual": "forecast",
        "is_derived": "false",
        "source_location": "8-K Item 1.01, lines 56-58",
        "source_snippet": "approximately $4.0 billion",
        "extract_regex": r"\$([0-9.]+)\s*billion",
        "scale": "1000",
        "confidence": "high",
        "caveat": "Expected contract value for initial approximately 15-year term.",
    },
    {
        "source_id": "APLD_2026Q2_10Q",
        "company": "APLD",
        "period": "FY2026Q2",
        "metric_name": "capacity_added_this_quarter_hpc",
        "raw_value": "100 MW completed and operational",
        "unit": "MW",
        "value_qualifier": "exact",
        "ownership_basis": "APLD Polaris Forge 1 ELN-02",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q lease note/business update, lines 1813-1816 and 2311-2314",
        "source_snippet": "100 MW ... completed and operational",
        "extract_regex": r"([0-9]+)\s*MW",
        "confidence": "medium",
        "caveat": "Filing has October/November wording in separate sections; quarter-level ledger treats it as FY2026Q2 capacity addition.",
    },
    {
        "source_id": "APLD_2026Q2_10Q",
        "company": "APLD",
        "period": "FY2026Q2",
        "metric_name": "revenue_conversion_hpc_total",
        "raw_value": "$85.0 million HPC Hosting revenue",
        "unit": "USD_millions",
        "value_qualifier": "approximately",
        "ownership_basis": "HPC Hosting segment",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q business update and MD&A, lines 2321-2323 and 2566-2567",
        "source_snippet": "$85.0 million ... HPC Hosting",
        "extract_regex": r"\$([0-9.]+)\s*million",
        "confidence": "high",
        "caveat": "Segment revenue includes tenant fit-out and rental revenue.",
    },
    {
        "source_id": "APLD_2026Q2_10Q",
        "company": "APLD",
        "period": "FY2026Q2",
        "metric_name": "revenue_conversion_tenant_fitout",
        "raw_value": "$73.0 million tenant fit-out services",
        "unit": "USD_millions",
        "value_qualifier": "approximately",
        "ownership_basis": "HPC Hosting segment",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q MD&A, lines 2566-2570",
        "source_snippet": "$73.0 million ... tenant fit-out",
        "extract_regex": r"\$([0-9.]+)\s*million",
        "confidence": "high",
        "caveat": "Tenant fit-out revenue is not recurring rental revenue.",
    },
    {
        "source_id": "APLD_2026Q2_10Q",
        "company": "APLD",
        "period": "FY2026Q2",
        "metric_name": "revenue_conversion_rental",
        "raw_value": "$12.0 million rental revenues",
        "unit": "USD_millions",
        "value_qualifier": "approximately",
        "ownership_basis": "Polaris Forge 1 ELN-02",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q MD&A, lines 2566-2567",
        "source_snippet": "$12.0 million ... rental revenues",
        "extract_regex": r"\$([0-9.]+)\s*million",
        "confidence": "high",
        "caveat": "Rental revenue appeared as ELN-02 fully energized during the quarter.",
    },
    {
        "source_id": "APLD_2026Q2_10Q",
        "company": "APLD",
        "period": "FY2026Q2",
        "metric_name": "minimum_contracted_lease_payments",
        "raw_value": "$15.551105 billion",
        "unit": "USD_millions",
        "value_qualifier": "exact",
        "ownership_basis": "lessor minimum contracted payments",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q Note 11 leases, lines 1817-1839",
        "source_snippet": "Total $15,551,105",
        "extract_regex": r"Total\s*\$?([0-9,]+)",
        "scale": "0.001",
        "confidence": "high",
        "caveat": "SEC table is in thousands and excludes operating-expense reimbursements and variable rent increases.",
    },
    {
        "source_id": "APLD_2026Q2_10Q",
        "company": "APLD",
        "period": "FY2026Q2",
        "metric_name": "contracted_capacity_polaris_forge_2",
        "raw_value": "200 MW",
        "unit": "MW",
        "value_qualifier": "exact",
        "ownership_basis": "APLD leased critical IT load",
        "forecast_or_actual": "actual",
        "is_derived": "false",
        "source_location": "10-Q lease note/business update, lines 1816 and 2321-2322",
        "source_snippet": "lease ... 200MW ... Polaris Forge 2",
        "extract_regex": r"([0-9]+)\s*MW",
        "confidence": "high",
        "caveat": "Executed lease commitment; facility remains under construction.",
    },
    {
        "source_id": "APLD_2026Q2_10Q",
        "company": "APLD",
        "period": "FY2026Q2",
        "metric_name": "expected_commencement_window_polaris_forge_2",
        "raw_value": "initial capacity in 2026 and full capacity in early 2027",
        "normalized_value": "initial:2026;full:2027-early",
        "unit": "calendar_window",
        "value_qualifier": "expected",
        "ownership_basis": "APLD Polaris Forge 2 project schedule",
        "forecast_or_actual": "forecast",
        "is_derived": "false",
        "source_location": "10-Q business update, lines 2321-2322",
        "source_snippet": "initial capacity in 2026 ... full ... early 2027",
        "extract_regex": r"initial capacity in 2026.*early 2027",
        "confidence": "high",
        "caveat": "Forward-looking project schedule.",
    },
]


def load_manifest(path: Path) -> Dict[str, dict]:
    with path.open(newline="", encoding="utf-8") as f:
        return {row["source_id"]: row for row in csv.DictReader(f)}


def decimal_from_match(match: re.Match[str], spec: dict) -> str:
    if spec.get("derive") == "sum_groups":
        value = sum(Decimal(g.replace(",", "")) for g in match.groups())
    else:
        value = Decimal(match.group(1).replace(",", ""))
    if "scale" in spec:
        value *= Decimal(spec["scale"])
    return format(value.normalize(), "f")


def extract_rows(specs: Iterable[dict], manifest: Dict[str, dict]) -> List[dict]:
    rows: List[dict] = []
    for spec in specs:
        source = manifest[spec["source_id"]]
        snippet = spec["source_snippet"]
        pattern = spec["extract_regex"]
        match = re.search(pattern, snippet, flags=re.IGNORECASE | re.DOTALL)

        support = "supported_regex_match" if match else "unsupported_no_regex_match"
        normalized_value = spec.get("normalized_value")
        if normalized_value is None:
            normalized_value = decimal_from_match(match, spec) if match else ""

        row = {field: "" for field in FIELDS}
        for field in FIELDS:
            if field in spec:
                row[field] = str(spec[field])
        row["normalized_value"] = str(normalized_value)
        row["source_url"] = source["source_url"]
        row["snippet_support_status"] = support
        rows.append(row)
    return rows


def write_rows(rows: List[dict], path: Path) -> None:
    with path.open("w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        writer.writerows(rows)


def main() -> None:
    manifest = load_manifest(MANIFEST_PATH)
    rows = extract_rows(ROW_SPECS, manifest)
    write_rows(rows, ROWS_PATH)
    print(f"wrote {len(rows)} rows to {ROWS_PATH}")


if __name__ == "__main__":
    main()
