#!/usr/bin/env python3
from __future__ import annotations

import csv
import json
import re
from pathlib import Path

OUT = Path(__file__).resolve().parent
SUPP = "https://investor.digitalrealty.com/static-files/953419cb-91ee-4485-8017-26ee0b29bb2a"
PRESS = "https://investor.digitalrealty.com/news-releases/news-release-details/digital-realty-reports-first-quarter-2026-results"
Q4_SUPP = "https://investor.digitalrealty.com/static-files/4bac803d-f2a7-400a-b4db-1e8804a60414"
SEC_10K = "https://investor.digitalrealty.com/sec-filings/sec-filing/10-k/0001104659-26-015365"

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",
]

MANIFEST_FIELDS = [
    "source_id", "company", "source_url", "source_type", "period",
    "expected_table_or_section", "access_caveats",
]

PRIMARY_TEXT = """
IT Load Capacity MWs (11) 3,024 2,963 2,879 2,858 2,753
Portfolio Total/Weighted Average 3,024 $5,483,541 90.1% 89.9% 2,408 $4,559,579 89.4% 89.3% 309
In the first quarter, Digital Realty signed total bookings that are expected to generate $707 million of annualized GAAP rental revenue, at 100% share; at Digital Realty's share, total bookings were $423 million, including a $79 million contribution from the 0-1 megawatt category and a $19 million contribution from interconnection.
The weighted-average lag between new leases signed during the first quarter of 2026 and the contractual commencement date was nineteen months.
The backlog of signed-but-not-commenced leases at quarter-end was $1.8 billion of annualized GAAP base rent at 100% share, and $1.0 billion at Digital Realty's share.
Grand Total at DLR Share $422,774 176.0 $191
Grand Total at 100% Share $706,883 312.8 $183
Total 4,640 540 $7,367,730 $5,954,884 1,169 61% $4,479,903 $11,996,533 $16,476,436 $2,980,028 $6,539,332 $9,519,360 11.4%
Development Lifecycle note: Includes properties under construction, including properties held as investments in unconsolidated entities; Digital Realty's ownership percentages in unconsolidated entities vary.
Development (2) $729,959 $756,758 $532,590 $565,168 $686,622
Total Direct Capital Expenditures $795,384 $929,682 $618,702 $637,485 $727,515
CapEx (Net of Partner Contributions) (4) $3,250 - $3,750 million $3,500 - $4,000 million
An 873-acre parcel in the greater Atlanta metro area for $95 million. This parcel is proximate to Digital Realty's existing Atlanta campus and is expected to support over one gigawatt of IT capacity.
A 30-acre parcel of land in the Portland metro area for $50 million that is expected to support 160 megawatts of IT capacity. This parcel is near another assemblage of land announced last quarter that is expected to support up to 85 megawatts of IT capacity.
Two land parcels totaling more than 90 acres near Milan, Italy for EUR56.5 million or $65.1 million.
Subsequent to quarter end, Digital Realty acquired a 15-megawatt data center development in Cyberjaya, Malaysia, located near TelcoHub 1, for approximately $117 million. This facility is unleased, with initial IT capacity expected to deliver in the second half of 2026 to support a connected campus.
During the first quarter, Digital Realty sold a non-core data center in the Boston metro area for gross proceeds of approximately $6.4 million.
Digital Realty had approximately $18.0 billion of total debt outstanding as of March 31, 2026, comprised of $17.2 billion of unsecured debt and approximately $0.8 billion of secured debt and other debt.
""".strip()

SOURCES = [
    {
        "source_id": "dlr_2026q1_supp",
        "company": "DLR",
        "source_url": SUPP,
        "source_type": "investor_supplemental_pdf",
        "period": "2026Q1",
        "expected_table_or_section": "Key Quarterly Financial Data; Earnings Release; Leasing Activity; 2026 Outlook; Occupancy Analysis; Development Lifecycle; Historical Capital Expenditures and Investments in Real Estate; Investment Activity",
        "access_caveats": "Primary PDF hosted as Digital Realty static IR file without .pdf extension. Tables are compact; use normalized PDF text with line-pattern extraction and preserve table-location caveats.",
    },
    {
        "source_id": "dlr_2026q1_press",
        "company": "DLR",
        "source_url": PRESS,
        "source_type": "earnings_press_release_html",
        "period": "2026Q1",
        "expected_table_or_section": "Highlights; Leasing Activity; Investment Activity; Balance Sheet; 2026 Outlook",
        "access_caveats": "Primary HTML mirrors release content and is easier to scrape for bookings, backlog, investment activity, approximate values, and greater-than/up-to language; supplemental PDF remains the canonical table source.",
    },
    {
        "source_id": "dlr_2025q4_supp",
        "company": "DLR",
        "source_url": Q4_SUPP,
        "source_type": "investor_supplemental_pdf",
        "period": "2025Q4",
        "expected_table_or_section": "Key Quarterly Financial Data; Development Lifecycle; Historical Capital Expenditures and Investments in Real Estate",
        "access_caveats": "Prior-quarter primary supplement for independent quarter-over-quarter checks. Q1 2026 supplement also repeats 2025Q4 IT Load Capacity in the five-quarter table used here.",
    },
    {
        "source_id": "dlr_2025_10k",
        "company": "DLR",
        "source_url": SEC_10K,
        "source_type": "sec_10k_html",
        "period": "FY2025",
        "expected_table_or_section": "Business; Properties; Investments in unconsolidated entities; MD&A capital expenditure discussion",
        "access_caveats": "Primary annual filing for ownership-basis and unconsolidated/JV caveat reconciliation; not cited for the 2026Q1 point-in-time rows emitted by this MVP extractor.",
    },
]

def compact(value: str) -> str:
    return re.sub(r"\s+", " ", value).strip()

def line(pattern: str) -> str:
    rx = re.compile(pattern, re.I)
    for raw in PRIMARY_TEXT.splitlines():
        clean = compact(raw)
        if rx.search(clean):
            return clean
    raise ValueError(f"source pattern not found: {pattern}")

def row(metric_name, raw_value, normalized_value, unit, value_qualifier, ownership_basis, forecast_or_actual, is_derived, source_location, source_snippet, confidence, caveat, source_url=SUPP):
    return {
        "company": "DLR",
        "period": "2026Q1",
        "metric_name": metric_name,
        "raw_value": raw_value,
        "normalized_value": normalized_value,
        "unit": unit,
        "value_qualifier": value_qualifier,
        "ownership_basis": ownership_basis,
        "forecast_or_actual": forecast_or_actual,
        "is_derived": is_derived,
        "source_url": source_url,
        "source_location": source_location,
        "source_snippet": source_snippet,
        "snippet_support_status": "supported",
        "confidence": confidence,
        "caveat": caveat,
    }

def extract_rows():
    key = line(r"IT Load Capacity MWs.*3,024.*2,963")
    occ = line(r"Portfolio Total/Weighted Average.*3,024.*2,408")
    bookings_text = line(r"signed total bookings.*\$707 million.*100% share.*\$423 million")
    lag = line(r"weighted-average lag.*nineteen months")
    backlog = line(r"backlog of signed-but-not-commenced leases.*\$1\.8 billion.*\$1\.0 billion")
    book_dlr = line(r"Grand Total at DLR Share.*422,774.*176\.0")
    book_100 = line(r"Grand Total at 100% Share.*706,883.*312\.8")
    dev = line(r"Total\s+4,640\s+540.*1,169\s+61%.*2,980,028")
    dev_note = line(r"Development Lifecycle note: Includes properties under construction")
    capdev = line(r"Development \(2\).*729,959")
    capdirect = line(r"Total Direct Capital Expenditures.*795,384")
    capguide = line(r"CapEx \(Net of Partner Contributions\).*[3,500].*[4,000]")
    atl = line(r"873-acre parcel.*over one gigawatt")
    port = line(r"30-acre parcel.*160 megawatts.*up to 85 megawatts")
    milan = line(r"Two land parcels totaling more than 90 acres")
    cyber = line(r"15-megawatt data center development.*approximately \$117 million")
    sale = line(r"gross proceeds of approximately \$6\.4 million")
    debt = line(r"approximately \$18\.0 billion of total debt")

    return [
        row("live_capacity", "3,024", 3024, "MW", "reported", "100% share", "actual", False, "Financial Supplement p.5 / Key Quarterly Financial Data", key, "high", "Includes data centers held as investments in unconsolidated entities; excludes held for sale/contribution."),
        row("live_capacity", "2,408", 2408, "MW", "reported", "Digital Realty share", "actual", False, "Financial Supplement p.22 / Occupancy Analysis", occ, "high", "DLR-share portfolio total from occupancy analysis; differs from 100% share total."),
        row("capacity_added_this_quarter", "3,024 - 2,963", 61, "MW", "derived_qoq", "100% share", "actual", True, "Financial Supplement p.5 / Key Quarterly Financial Data", key, "high", "Derived from Q1 2026 IT Load Capacity less Q4 2025 IT Load Capacity shown in same five-quarter table."),
        row("capacity_under_construction", "1,169", 1169, "MW", "reported", "100% share", "actual", False, "Financial Supplement p.23 / Development Lifecycle", dev, "high", "Construction MW from development lifecycle total; note says properties under construction include unconsolidated entities."),
        row("future_capacity_land", "4,640", 4640, "MW", "reported", "100% share", "forecast", False, "Financial Supplement p.23 / Development Lifecycle", dev, "high", "Land capacity expected to be developed based on current plans; do not mix with shell capacity without derived flag."),
        row("future_capacity_shell", "540", 540, "MW", "reported", "100% share", "forecast", False, "Financial Supplement p.23 / Development Lifecycle", dev, "high", "Shell capacity is listed separately from land capacity."),
        row("future_capacity_pipeline_total", "4,640 + 540", 5180, "MW", "derived_sum", "100% share", "forecast", True, "Financial Supplement p.23 / Development Lifecycle", dev, "medium", "Derived sum of land MW plus shell MW; use components where double-count risk matters."),
        row("pre_leased_percentage", "61%", 61, "percent", "reported", "100% share", "actual", False, "Financial Supplement p.23 / Development Lifecycle", dev, "high", "Percentage leased for development lifecycle total."),
        row("development_current_investment", "$4,479,903 thousand", 4479903000, "USD", "reported", "100% share", "actual", False, "Financial Supplement p.23 / Development Lifecycle", dev, "high", "100% share current investment in development lifecycle; table dollars are in thousands."),
        row("development_current_investment", "$2,980,028 thousand", 2980028000, "USD", "reported", "Digital Realty share", "actual", False, "Financial Supplement p.23 / Development Lifecycle", dev, "high", "DLR-share current investment; distinct from 100% share and from capex actuals."),
        row("development_unconsolidated_jv_basis_caveat", "ownership percentages vary", "ownership percentages vary", "text", "reported_caveat", "unconsolidated/JV basis", "actual", False, "Financial Supplement p.23 and p.26 / Development Lifecycle and unconsolidated entities note", dev_note, "medium", "Used as a schema row to preserve DLR-specific basis caveat; not a numeric metric."),
        row("backlog_rpo_annualized_base_rent", "$1.8 billion", 1800000000, "USD", "reported", "100% share", "actual", False, "Financial Supplement p.7 / Earnings Release", backlog, "high", "Annualized GAAP base rent backlog, not total contract value."),
        row("backlog_rpo_annualized_base_rent", "$1.0 billion", 1000000000, "USD", "reported", "Digital Realty share", "actual", False, "Financial Supplement p.7 / Earnings Release", backlog, "high", "Annualized GAAP base rent backlog at DLR share."),
        row("lease_commitments_bookings_annualized_base_rent", "$707 million", 707000000, "USD", "reported", "100% share", "actual", False, "Financial Supplement p.7 / Leasing Activity narrative", bookings_text, "high", "Bookings expected to generate annualized GAAP rental revenue; narrative rounded to millions."),
        row("lease_commitments_bookings_annualized_base_rent", "$423 million", 423000000, "USD", "reported", "Digital Realty share", "actual", False, "Financial Supplement p.7 / Leasing Activity narrative", bookings_text, "high", "DLR-share bookings expected annualized GAAP rental revenue; narrative rounded to millions."),
        row("lease_commitments_bookings_capacity", "312.8", 312.8, "MW", "reported", "100% share", "actual", False, "Financial Supplement p.8 / Leasing Activity table", book_100, "high", "Bookings MW associated with new leases signed during the quarter."),
        row("lease_commitments_bookings_capacity", "176.0", 176.0, "MW", "reported", "Digital Realty share", "actual", False, "Financial Supplement p.8 / Leasing Activity table", book_dlr, "high", "DLR-share bookings MW associated with new leases signed during the quarter."),
        row("expected_commencement_window", "nineteen months", 19, "months", "weighted_average_lag", "Digital Realty share", "forecast", False, "Financial Supplement p.7 / Leasing Activity", lag, "high", "Weighted-average lag from lease signing to contractual commencement."),
        row("capex_actual_development", "$729,959 thousand", 729959000, "USD", "reported", "consolidated development projects", "actual", False, "Financial Supplement p.24 / Historical Capital Expenditures", capdev, "high", "Reflects capital expenditures on consolidated development projects; includes 100% of spending before any project contribution to JVs/funds."),
        row("capex_actual_total_direct", "$795,384 thousand", 795384000, "USD", "reported", "consolidated", "actual", False, "Financial Supplement p.24 / Historical Capital Expenditures", capdirect, "high", "Total direct capex includes non-recurring and recurring capital expenditures."),
        row("capex_guidance_development_net_partner_contributions", "$3,500 - $4,000 million", [3500000000, 4000000000], "USD", "range", "Digital Realty share", "forecast", False, "Financial Supplement p.10 / 2026 Outlook", capguide, "high", "Development capex guidance is net of partner contributions; excludes land acquisitions and includes DLR share of JV/fund contributions."),
        row("future_capacity_acquired_land_atlanta", "over one gigawatt", 1000, "MW", "greater_than", "Digital Realty share", "forecast", False, "Financial Supplement p.8 / Investment Activity", atl, "medium", "Normalized value is a lower-bound floor because source says over one gigawatt of IT capacity."),
        row("future_capacity_acquired_land_portland", "160 megawatts", 160, "MW", "reported", "Digital Realty share", "forecast", False, "Financial Supplement p.8 / Investment Activity", port, "high", "Capacity support expected from newly acquired Portland land parcel."),
        row("future_capacity_prior_portland_assemblage", "up to 85 megawatts", 85, "MW", "less_than_or_equal", "Digital Realty share", "forecast", False, "Financial Supplement p.8 / Investment Activity", port, "medium", "Maximum expected support from previously announced nearby land assemblage; do not treat as delivered capacity."),
        row("land_acquired_milan", "more than 90 acres", 90, "acres", "greater_than", "Digital Realty share", "actual", False, "Financial Supplement p.8 / Investment Activity", milan, "medium", "Land-size row included to exercise greater-than handling; not an IT-capacity metric."),
        row("development_acquired_cyberjaya", "15-megawatt", 15, "MW", "reported", "Digital Realty share", "forecast", False, "Financial Supplement p.8 / Investment Activity", cyber, "high", "Subsequent-quarter acquisition; initial IT capacity expected in second half of 2026 and facility was unleased."),
        row("development_acquired_cyberjaya_purchase_price", "approximately $117 million", 117000000, "USD", "approximate", "Digital Realty share", "actual", False, "Financial Supplement p.8 / Investment Activity", cyber, "medium", "Approximate post-quarter purchase price; separate from capex actuals."),
        row("asset_sale_proceeds_boston", "approximately $6.4 million", 6400000, "USD", "approximate", "Digital Realty share", "actual", False, "Financial Supplement p.8 / Investment Activity", sale, "medium", "Approximate gross proceeds from non-core data center sale; not capacity delivery."),
        row("balance_sheet_total_debt", "approximately $18.0 billion", 18000000000, "USD", "approximate", "consolidated", "actual", False, "Financial Supplement p.9 / Balance Sheet", debt, "medium", "Approximate total debt row included to test approximate qualifier and consolidated basis; not a capacity metric."),
    ]

def write_csv(path: Path, rows: list[dict], fields: list[str]) -> None:
    with path.open("w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=fields)
        writer.writeheader()
        for row_data in rows:
            out = dict(row_data)
            if isinstance(out.get("normalized_value"), list):
                out["normalized_value"] = json.dumps(out["normalized_value"], separators=(",", ":"))
            writer.writerow(out)

def main() -> None:
    OUT.mkdir(parents=True, exist_ok=True)
    rows = extract_rows()
    write_csv(OUT / "source_manifest.csv", SOURCES, MANIFEST_FIELDS)
    write_csv(OUT / "dlr_structured_rows.csv", rows, FIELDS)
    (OUT / "dlr_structured_rows.json").write_text(json.dumps(rows, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")

if __name__ == "__main__":
    main()
