"""
Build a Sales Capacity Stress Test model in Google Sheets.

Question answered: Do we have enough ramped AE capacity to hit bookings targets?
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

from .period_helper import PeriodConfig, build_period_table


DEFAULT_PACING = [0.15, 0.20, 0.30, 0.35]
DEFAULT_RAMP_PRODUCTIVITY = [0.25, 0.50, 0.75, 1.0]  # M1-2, M3-4, M5-6, Ramped
DEFAULT_SEASONALITY = [1.0, 1.0, 1.0, 1.0]


def _col_letter(idx: int) -> str:
    """Convert zero-based index to Excel-style column letter."""
    result = ""
    idx_val = idx
    while True:
        idx_val, rem = divmod(idx_val, 26)
        result = chr(ord("A") + rem) + result
        if idx_val == 0:
            break
        idx_val -= 1
    return result


@dataclass
class CapacityInputs:
    """Inputs for the Sales Capacity Stress Test model."""
    annual_target: float
    pacing_q1: float = 0.15
    pacing_q2: float = 0.20
    pacing_q3: float = 0.30
    pacing_q4: float = 0.35
    avg_deal_size: float = 100000.0
    win_rate: float = 0.25
    starting_aes: int = 4
    net_hires_list: List[int] = field(default_factory=lambda: [0, 1, 1, 1])
    hire_month: int = 2  # 1=start, 2=mid, 3=end of quarter
    ramp_months: int = 6
    ramp_m1_2: float = 0.25
    ramp_m3_4: float = 0.50
    ramp_m5_6: float = 0.75
    ramp_full: float = 1.0
    ramped_quota: float = 250000.0
    seasonality_q1: float = 1.0
    seasonality_q2: float = 1.0
    seasonality_q3: float = 1.0
    seasonality_q4: float = 1.0
    first_fy: int = 26
    fy_start_month: int = 1
    planning_years: int = 1  # 1–3 fiscal years to plan
    yoy_growth_pct: float = 0.0
    as_of_date: str = ""
    # Idempotency handling
    tab_suffix: str = ""  # e.g., "_v2" to append to tab names
    idempotency_action: str = ""  # "replace" or "suffix"

    @property
    def pacing(self) -> List[float]:
        return [self.pacing_q1, self.pacing_q2, self.pacing_q3, self.pacing_q4]

    @property
    def net_hires(self) -> List[int]:
        return self.net_hires_list

    @property
    def ramp_productivity(self) -> List[float]:
        return [self.ramp_m1_2, self.ramp_m3_4, self.ramp_m5_6, self.ramp_full]

    @property
    def seasonality(self) -> List[float]:
        return [self.seasonality_q1, self.seasonality_q2, self.seasonality_q3, self.seasonality_q4]

    @property
    def horizon_periods(self) -> int:
        return self.planning_years * 4

    def __post_init__(self) -> None:
        # Validate pacing sums to approximately 100%
        pacing_sum = sum(self.pacing)
        if not (0.95 <= pacing_sum <= 1.05):
            raise ValueError(f"Quarterly pacing must sum to ~100%, got {pacing_sum * 100:.1f}%")

        if self.fy_start_month < 1 or self.fy_start_month > 12:
            raise ValueError("fy_start_month must be between 1 and 12.")

        if self.hire_month not in (1, 2, 3):
            raise ValueError("hire_month must be 1 (start), 2 (mid), or 3 (end)")

        if self.planning_years < 1 or self.planning_years > 3:
            raise ValueError("planning_years must be between 1 and 3.")

        expected_hires = self.planning_years * 4
        if len(self.net_hires_list) != expected_hires:
            raise ValueError(
                f"net_hires_list must have {expected_hires} entries "
                f"({self.planning_years} years × 4 quarters), got {len(self.net_hires_list)}."
            )


def _quote_tab(tab_name: str) -> str:
    """Escape single quotes in tab names for sheet references."""
    escaped = tab_name.replace("'", "''")
    return f"'{escaped}'"


def _sheet_cell(tab_name: str, cell_ref: str) -> str:
    """Build a quoted sheet cell reference."""
    return f"{_quote_tab(tab_name)}!{cell_ref}"


def _hex_to_color(hex_value: str) -> Dict[str, float]:
    """Convert hex color to RGB normalized to 0-1."""
    hex_value = hex_value.lstrip("#")
    r = int(hex_value[0:2], 16) / 255.0
    g = int(hex_value[2:4], 16) / 255.0
    b = int(hex_value[4:6], 16) / 255.0
    return {"red": r, "green": g, "blue": b}


def build_capacity_stress(
    inputs: CapacityInputs,
    sheets_service,
    spreadsheet_id: Optional[str] = None,
    title: str = "Sales Capacity Stress Test",
) -> Dict[str, Any]:
    """
    Build the Sales Capacity Stress Test model.

    Creates 3 tabs:
    - Capacity_Assumptions: User inputs with readme panel
    - Capacity_Calc: Calculations with period columns
    - Capacity_Dashboard: Key outputs and RAG status

    Args:
        inputs: CapacityInputs with all model parameters
        sheets_service: Google Sheets API service client
        spreadsheet_id: Optional existing spreadsheet to update
        title: Title for new spreadsheet if created

    Returns:
        Dict with spreadsheet_id, url, tabs_created, validation status
    """
    # Apply tab suffix if provided (for idempotency with suffix mode)
    suffix = inputs.tab_suffix or ""
    assumptions_tab = f"Capacity_Assumptions{suffix}"
    calc_tab = f"Capacity_Calc{suffix}"
    dashboard_tab = f"Capacity_Dashboard{suffix}"

    # Determine if we should replace existing tabs
    replace_existing = inputs.idempotency_action == "replace"

    # Generate period labels
    period_config = PeriodConfig(
        first_fy=inputs.first_fy,
        fy_start_month=inputs.fy_start_month,
        granularity="quarterly",
        horizon_periods=inputs.horizon_periods,
    )
    periods = build_period_table(period_config)

    # Create or get spreadsheet
    if spreadsheet_id is None:
        create_resp = sheets_service.spreadsheets().create(
            body={"properties": {"title": title}}
        ).execute()
        spreadsheet_id = create_resp["spreadsheetId"]
        created = True
    else:
        created = False

    # Ensure tabs exist (replace if idempotency action is "replace")
    _ensure_sheet(sheets_service, spreadsheet_id, assumptions_tab, replace=replace_existing)
    _ensure_sheet(sheets_service, spreadsheet_id, calc_tab, replace=replace_existing)
    _ensure_sheet(sheets_service, spreadsheet_id, dashboard_tab, replace=replace_existing)

    # Write Assumptions tab
    assumptions_rows = _build_assumptions_rows(inputs)
    _write_rows(sheets_service, spreadsheet_id, assumptions_tab, assumptions_rows)

    # Write Calc tab
    calc_rows = _build_calc_rows(inputs, periods, assumptions_tab)
    _write_rows(sheets_service, spreadsheet_id, calc_tab, calc_rows)

    # Write Dashboard tab
    dashboard_rows = _build_dashboard_rows(inputs, periods, calc_tab)
    _write_rows(sheets_service, spreadsheet_id, dashboard_tab, dashboard_rows)

    # Apply styling
    _apply_capacity_style(sheets_service, spreadsheet_id, assumptions_tab, calc_tab, dashboard_tab, len(periods), inputs.planning_years)

    # Validate
    validation = _validate_capacity(sheets_service, spreadsheet_id, calc_tab, len(periods))

    url = f"https://docs.google.com/spreadsheets/d/{spreadsheet_id}"

    return {
        "spreadsheet_id": spreadsheet_id,
        "url": url,
        "tabs_created": [assumptions_tab, calc_tab, dashboard_tab],
        "created": created,
        "validation": validation,
    }


def _delete_sheet_if_exists(service, spreadsheet_id: str, sheet_name: str) -> bool:
    """Delete a sheet if it exists. Returns True if deleted."""
    try:
        ss = service.spreadsheets().get(spreadsheetId=spreadsheet_id).execute()
        for sheet in ss.get("sheets", []):
            if sheet["properties"]["title"] == sheet_name:
                sheet_id = sheet["properties"]["sheetId"]
                service.spreadsheets().batchUpdate(
                    spreadsheetId=spreadsheet_id,
                    body={
                        "requests": [
                            {"deleteSheet": {"sheetId": sheet_id}}
                        ]
                    }
                ).execute()
                return True
    except Exception:
        pass
    return False


def _ensure_sheet(service, spreadsheet_id: str, sheet_name: str, replace: bool = False) -> int:
    """Ensure a sheet exists, creating it if needed. Returns sheet ID.

    Args:
        service: Sheets API service
        spreadsheet_id: Spreadsheet ID
        sheet_name: Name of the sheet
        replace: If True, delete existing sheet first
    """
    try:
        ss = service.spreadsheets().get(spreadsheetId=spreadsheet_id).execute()
        for sheet in ss.get("sheets", []):
            if sheet["properties"]["title"] == sheet_name:
                if replace:
                    # Delete existing sheet
                    sheet_id = sheet["properties"]["sheetId"]
                    service.spreadsheets().batchUpdate(
                        spreadsheetId=spreadsheet_id,
                        body={
                            "requests": [
                                {"deleteSheet": {"sheetId": sheet_id}}
                            ]
                        }
                    ).execute()
                    break  # Fall through to create new sheet
                else:
                    return sheet["properties"]["sheetId"]
    except Exception:
        pass

    # Create the sheet
    resp = service.spreadsheets().batchUpdate(
        spreadsheetId=spreadsheet_id,
        body={
            "requests": [
                {"addSheet": {"properties": {"title": sheet_name}}}
            ]
        }
    ).execute()

    return resp["replies"][0]["addSheet"]["properties"]["sheetId"]


def _write_rows(service, spreadsheet_id: str, sheet_name: str, rows: List[List[Any]]) -> None:
    """Write rows to a sheet."""
    if not rows:
        return

    # Determine range
    num_cols = max(len(row) for row in rows)
    end_col = _col_letter(num_cols - 1)
    range_str = f"{sheet_name}!A1:{end_col}{len(rows)}"

    # Pad rows to same length
    padded_rows = []
    for row in rows:
        padded = list(row) + [""] * (num_cols - len(row))
        padded_rows.append(padded)

    service.spreadsheets().values().update(
        spreadsheetId=spreadsheet_id,
        range=range_str,
        valueInputOption="USER_ENTERED",
        body={"values": padded_rows}
    ).execute()


def _assumptions_row_map(planning_years: int) -> Dict[str, int]:
    """Compute 1-based sheet row numbers for key assumptions cells.

    Row positions shift when planning_years changes because the net-hires
    block has ``planning_years * 4`` rows instead of a fixed 8.
    """
    r = 1  # current row (1-indexed)
    # README (4 rows + blank)
    r += 5
    # TARGETS header
    r += 1
    pos: Dict[str, int] = {}
    pos["annual_target"] = r; r += 1       # Annual Bookings Target
    pos["pacing_q1"] = r; r += 1
    pos["pacing_q2"] = r; r += 1
    pos["pacing_q3"] = r; r += 1
    pos["pacing_q4"] = r; r += 1
    pos["avg_deal_size"] = r; r += 1
    pos["win_rate"] = r; r += 1
    r += 1  # blank
    # HEADCOUNT header
    r += 1
    pos["starting_aes"] = r; r += 1
    pos["net_hires_start"] = r             # First net-hires row
    r += planning_years * 4                # One row per quarter
    pos["net_hires_end"] = r - 1           # Last net-hires row
    pos["hire_month"] = r; r += 1
    r += 1  # blank
    # RAMP header
    r += 1
    pos["ramp_months"] = r; r += 1
    pos["ramp_m1_2"] = r; r += 1
    pos["ramp_m3_4"] = r; r += 1
    pos["ramp_m5_6"] = r; r += 1
    pos["ramp_full"] = r; r += 1
    pos["ramped_quota"] = r; r += 1
    r += 1  # blank
    # SEASONALITY header
    r += 1
    pos["seasonality_q1"] = r; r += 1
    pos["seasonality_q2"] = r; r += 1
    pos["seasonality_q3"] = r; r += 1
    pos["seasonality_q4"] = r; r += 1
    r += 1  # blank
    # PERIOD SETTINGS header
    r += 1
    pos["first_fy"] = r; r += 1
    pos["fy_start_month"] = r; r += 1
    pos["planning_years"] = r; r += 1
    pos["yoy_growth_pct"] = r; r += 1
    pos["as_of_date"] = r
    return pos


def _build_assumptions_rows(inputs: CapacityInputs) -> List[List[Any]]:
    """Build the Assumptions tab content."""
    rows = []

    # README section
    rows.append(["CAPACITY STRESS TEST - ASSUMPTIONS"])
    rows.append(["What this model answers:", "Do you have enough ramped AE capacity to hit your bookings targets?"])
    rows.append(["Assumptions you can change:", "Target, pacing, headcount, ramp curve, seasonality"])
    rows.append(["FY convention:", "FY label uses the fiscal year end year (e.g., FY26 ends in 2026)"])
    rows.append([])

    # TARGETS section
    rows.append(["TARGETS"])
    rows.append(["Annual Bookings Target", inputs.annual_target])
    rows.append(["Q1 Pacing", inputs.pacing_q1])
    rows.append(["Q2 Pacing", inputs.pacing_q2])
    rows.append(["Q3 Pacing", inputs.pacing_q3])
    rows.append(["Q4 Pacing", inputs.pacing_q4])
    rows.append(["Avg Deal Size", inputs.avg_deal_size])
    rows.append(["Win Rate", inputs.win_rate])
    rows.append([])

    # HEADCOUNT section
    rows.append(["HEADCOUNT"])
    rows.append(["Starting AEs (beginning of FY)", inputs.starting_aes])
    for yr in range(inputs.planning_years):
        fy = inputs.first_fy + yr
        for q in range(1, 5):
            idx = yr * 4 + (q - 1)
            label = f"Net Hires Q{q}" if yr == 0 else f"Net Hires FY{fy} Q{q}"
            rows.append([label, inputs.net_hires_list[idx]])
    rows.append(["Hire Month in Quarter", inputs.hire_month, "(1=start, 2=mid, 3=end)"])
    rows.append([])

    # RAMP section
    rows.append(["RAMP & PRODUCTIVITY"])
    rows.append(["Ramp Months", inputs.ramp_months])
    rows.append(["M1-2 Productivity", inputs.ramp_m1_2])
    rows.append(["M3-4 Productivity", inputs.ramp_m3_4])
    rows.append(["M5-6 Productivity", inputs.ramp_m5_6])
    rows.append(["Fully Ramped Productivity", inputs.ramp_full])
    rows.append(["Ramped AE Quota ($/qtr)", inputs.ramped_quota])
    rows.append([])

    # SEASONALITY section
    rows.append(["SEASONALITY"])
    rows.append(["Q1 Seasonality", inputs.seasonality_q1])
    rows.append(["Q2 Seasonality", inputs.seasonality_q2])
    rows.append(["Q3 Seasonality", inputs.seasonality_q3])
    rows.append(["Q4 Seasonality", inputs.seasonality_q4])
    rows.append([])

    # PERIOD SETTINGS section
    rows.append(["PERIOD SETTINGS"])
    rows.append(["First Fiscal Year", inputs.first_fy])
    rows.append(["FY Start Month", inputs.fy_start_month])
    rows.append(["Planning Years", inputs.planning_years])
    rows.append(["YoY Growth %", inputs.yoy_growth_pct])
    rows.append(["As-Of Date", inputs.as_of_date or ""])

    return rows


def _build_calc_rows(inputs: CapacityInputs, periods: list, assumptions_tab: str) -> List[List[Any]]:
    """Build the Calc tab with formulas."""
    rows = []
    num_periods = len(periods)

    # Dynamic assumption-tab row positions
    a = _assumptions_row_map(inputs.planning_years)

    def _a(key: str) -> str:
        """Shorthand for an absolute assumptions-tab cell reference."""
        return _sheet_cell(assumptions_tab, f"$B${a[key]}")

    # Header row with period labels
    header = ["Type", "Category", "Metric"]
    for p in periods:
        header.append(p.label)
    rows.append(header)

    # Period dates (written as values, not formulas)
    start_row = ["", "", "Period Start"]
    end_row = ["", "", "Period End"]
    for p in periods:
        start_row.append(p.start_date.isoformat())
        end_row.append(p.end_date.isoformat())
    rows.append(start_row)
    rows.append(end_row)

    rows.append([])  # Blank row

    # TARGETS section
    rows.append(["TARGETS", "", ""])
    target_row = ["Target", "ACV", "Bookings Target"]
    for i, p in enumerate(periods):
        qtr_in_fy = p.period_num
        yr_offset = p.fy - inputs.first_fy
        base_target = _a("annual_target")
        if yr_offset == 0:
            annual_cell = base_target
        else:
            # Chained growth: FY(N+k) = base * (1 + growth)^k
            annual_cell = f"({base_target}*(1+{_a('yoy_growth_pct')})^{yr_offset})"

        pacing_row = a["pacing_q1"] + (qtr_in_fy - 1)
        pacing_cell = _sheet_cell(assumptions_tab, f"$B${pacing_row}")

        target_row.append(f"={annual_cell}*{pacing_cell}")
    rows.append(target_row)

    # Required Deals = Target / ASP
    deals_row = ["Target", "Deals", "Required C/W Deals"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        deals_row.append(f"={col}6/{_a('avg_deal_size')}")
    rows.append(deals_row)

    # Required Opps = Deals / Win Rate
    opps_row = ["Target", "Opps", "Required Opps"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        opps_row.append(f"={col}7/{_a('win_rate')}")
    rows.append(opps_row)

    # Required Pipeline = Opps * ASP
    pipeline_row = ["Target", "Pipeline", "Required Pipeline"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        pipeline_row.append(f"={col}8*{_a('avg_deal_size')}")
    rows.append(pipeline_row)

    rows.append([])  # Blank row

    # HEADCOUNT section
    rows.append(["HEADCOUNT", "", ""])

    # Starting AEs
    starting_row = ["AE", "Count", "Starting AEs"]
    for i, p in enumerate(periods):
        if i == 0:
            starting_row.append(f"={_a('starting_aes')}")
        else:
            prev_col = _col_letter(3 + i - 1)
            starting_row.append(f"={prev_col}14")
    rows.append(starting_row)

    # Net Hires — reference the dynamic assumptions rows
    hires_row = ["AE", "Count", "Net Hires"]
    for i, p in enumerate(periods):
        yr_offset = p.fy - inputs.first_fy
        qtr_in_fy = p.period_num
        hires_cell_row = a["net_hires_start"] + yr_offset * 4 + (qtr_in_fy - 1)
        hires_row.append(f"={_sheet_cell(assumptions_tab, f'$B${hires_cell_row}')}")
    rows.append(hires_row)

    # Ending AEs = Starting + Net Hires
    ending_row = ["AE", "Count", "Ending AEs"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        ending_row.append(f"={col}12+{col}13")
    rows.append(ending_row)

    rows.append([])  # Blank row

    # RAMP section - Track hire cohorts with progressive ramp
    rows.append(["RAMP", "", ""])

    # New Hire FTE Weight = 1 - (HireMonth - 1) / 3
    weight_row = ["AE", "Weight", "New Hire FTE Weight"]
    for i in range(num_periods):
        weight_row.append(f"=1-({_a('hire_month')}-1)/3")
    rows.append(weight_row)

    # Track each quarter's hire cohort productivity across periods
    ramp_months_cell = _a("ramp_months")
    ramp_m1_2_cell = _a("ramp_m1_2")
    ramp_m3_4_cell = _a("ramp_m3_4")
    ramp_m5_6_cell = _a("ramp_m5_6")
    ramp_full_cell = _a("ramp_full")
    quarters_to_full = f"CEILING({ramp_months_cell}/2)"

    for cohort_idx in range(num_periods):
        cohort_period = periods[cohort_idx]
        cohort_label = f"FY{cohort_period.fy} Q{cohort_period.period_num} Hires FTE"
        cohort_row = ["AE", "Cohort", cohort_label]
        cohort_col = _col_letter(3 + cohort_idx)

        for period_idx in range(num_periods):
            if period_idx < cohort_idx:
                cohort_row.append(0)
            else:
                qtrs_since_hire = period_idx - cohort_idx
                if qtrs_since_hire == 0:
                    ramp_factor = ramp_m1_2_cell
                elif qtrs_since_hire == 1:
                    ramp_factor = f"IF({quarters_to_full}>1,{ramp_m3_4_cell},{ramp_full_cell})"
                else:
                    ramp_factor = f"IF({quarters_to_full}>{qtrs_since_hire},{ramp_m5_6_cell},{ramp_full_cell})"

                if period_idx == cohort_idx:
                    period_col = _col_letter(3 + period_idx)
                    cohort_row.append(f"={cohort_col}13*{period_col}17*{ramp_factor}")
                else:
                    cohort_row.append(f"={cohort_col}13*{ramp_factor}")
        rows.append(cohort_row)

    # Total New Hire FTE = Sum of all cohort rows
    new_hire_fte_row = ["AE", "FTE", "Total New Hire FTE"]
    cohort_rows = list(range(18, 18 + num_periods))  # Rows 18+ for cohorts
    for i in range(num_periods):
        col = _col_letter(3 + i)
        sum_refs = "+".join([f"{col}{r}" for r in cohort_rows])
        new_hire_fte_row.append(f"={sum_refs}")
    rows.append(new_hire_fte_row)

    # Tenured AE FTE = Starting AEs * Full Productivity
    tenured_fte_row = ["AE", "FTE", "Tenured AE FTE"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        tenured_fte_row.append(f"={col}12*{_a('ramp_full')}")
    rows.append(tenured_fte_row)

    # Total Effective AE FTE = New Hire FTE + Tenured FTE
    total_new_hire_row = 18 + num_periods
    tenured_row_num = total_new_hire_row + 1
    total_fte_row = ["AE", "FTE", "Total Effective AE FTE"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        total_fte_row.append(f"={col}{total_new_hire_row}+{col}{tenured_row_num}")
    rows.append(total_fte_row)

    rows.append([])  # Blank row

    num_cohorts = num_periods
    total_fte_row_num = 18 + num_cohorts + 2

    # CAPACITY section
    rows.append(["CAPACITY", "", ""])

    # Seasonality factor
    seasonality_row = ["Capacity", "Factor", "Seasonality"]
    for i, p in enumerate(periods):
        qtr_in_fy = p.period_num
        seas_cell_row = a["seasonality_q1"] + (qtr_in_fy - 1)
        seasonality_row.append(f"={_sheet_cell(assumptions_tab, f'$B${seas_cell_row}')}")
    rows.append(seasonality_row)
    seasonality_row_num = total_fte_row_num + 3  # blank + CAPACITY + seasonality

    # AE Capacity = Effective FTE * Ramped Quota * Seasonality
    capacity_row = ["Capacity", "ACV", "AE Capacity ($/qtr)"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        capacity_row.append(f"={col}{total_fte_row_num}*{_a('ramped_quota')}*{col}{seasonality_row_num}")
    rows.append(capacity_row)
    capacity_row_num = seasonality_row_num + 1

    # Gap = Capacity - Target
    gap_row = ["Capacity", "ACV", "Gap (Capacity - Target)"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        gap_row.append(f"={col}{capacity_row_num}-{col}6")
    rows.append(gap_row)
    gap_row_num = capacity_row_num + 1

    # RAG Status
    rag_row = ["Capacity", "Status", "RAG Status"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        rag_row.append(f'=IF({col}{gap_row_num}>=0,"GREEN",IF({col}{gap_row_num}>-{col}6*0.1,"YELLOW","RED"))')
    rows.append(rag_row)

    rows.append([])  # Blank row

    # STRESS SCENARIOS section
    rows.append(["STRESS", "", ""])

    # At 90% Productivity
    stress_90_row = ["Stress", "ACV", "Gap at 90% Productivity"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        stress_90_row.append(f"={col}{capacity_row_num}*0.9-{col}6")
    rows.append(stress_90_row)

    # At 80% Productivity
    stress_80_row = ["Stress", "ACV", "Gap at 80% Productivity"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        stress_80_row.append(f"={col}{capacity_row_num}*0.8-{col}6")
    rows.append(stress_80_row)

    # Deals per Ramped AE
    deals_per_ae_row = ["Stress", "Ratio", "Deals per Ramped AE"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        deals_per_ae_row.append(f"=IFERROR({col}7/{col}{total_fte_row_num},0)")
    rows.append(deals_per_ae_row)

    return rows


def _build_dashboard_rows(inputs: CapacityInputs, periods: list, calc_tab: str) -> List[List[Any]]:
    """Build the Dashboard tab with key metrics."""
    rows = []
    num_periods = len(periods)

    # Calculate calc tab row numbers (must match _build_calc_rows logic)
    num_cohorts = num_periods
    calc_total_fte_row = 18 + num_cohorts + 2  # After cohorts + total new hire + tenured
    calc_seasonality_row = calc_total_fte_row + 3  # blank + CAPACITY + seasonality
    calc_capacity_row = calc_seasonality_row + 1
    calc_gap_row = calc_capacity_row + 1
    calc_rag_row = calc_gap_row + 1

    # Header
    header = ["Metric"]
    for p in periods:
        header.append(p.label)
    rows.append(header)

    rows.append([])  # Blank row

    # Key metrics
    rows.append(["KEY METRICS"])

    # Bookings Target
    target_row = ["Bookings Target"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        target_row.append(f"={_sheet_cell(calc_tab, f'{col}6')}")
    rows.append(target_row)

    # AE Capacity
    capacity_row = ["AE Capacity"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        capacity_row.append(f"={_sheet_cell(calc_tab, f'{col}{calc_capacity_row}')}")
    rows.append(capacity_row)

    # Gap
    gap_row = ["Gap (Capacity - Target)"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        gap_row.append(f"={_sheet_cell(calc_tab, f'{col}{calc_gap_row}')}")
    rows.append(gap_row)

    # RAG Status
    rag_row = ["Status"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        rag_row.append(f"={_sheet_cell(calc_tab, f'{col}{calc_rag_row}')}")
    rows.append(rag_row)

    rows.append([])  # Blank row

    # HEADCOUNT SUMMARY
    rows.append(["HEADCOUNT"])

    fte_row = ["Effective AE FTE"]
    for i in range(num_periods):
        col = _col_letter(3 + i)
        fte_row.append(f"={_sheet_cell(calc_tab, f'{col}{calc_total_fte_row}')}")
    rows.append(fte_row)

    rows.append([])  # Blank row

    # SANITY CHECK section (hidden by default - self-diagnostic)
    rows.append(["SANITY CHECK"])
    # Check: Gap = Capacity - Target (should be TRUE for all periods)
    check_row = ["Check (Gap = Cap - Target)"]
    for i in range(num_periods):
        col = _col_letter(1 + i)  # Dashboard columns start at B
        # Gap row is row 6, Capacity is row 5, Target is row 4 in dashboard
        check_row.append(f'=IF(ABS({col}6-({col}5-{col}4))<0.01,"✓","✗")')
    rows.append(check_row)

    return rows


def _get_sheet_id(service, spreadsheet_id: str, sheet_name: str) -> int:
    """Get the sheet ID for a named sheet."""
    ss = service.spreadsheets().get(spreadsheetId=spreadsheet_id).execute()
    for sheet in ss.get("sheets", []):
        if sheet["properties"]["title"] == sheet_name:
            return sheet["properties"]["sheetId"]
    raise ValueError(f"Sheet {sheet_name} not found")


def _apply_capacity_style(
    service,
    spreadsheet_id: str,
    assumptions_tab: str,
    calc_tab: str,
    dashboard_tab: str,
    num_periods: int,
    planning_years: int = 1,
) -> None:
    """Apply professional formatting to all tabs."""
    requests = []

    # Style each tab
    for tab_name in [assumptions_tab, calc_tab, dashboard_tab]:
        try:
            sheet_id = _get_sheet_id(service, spreadsheet_id, tab_name)
        except ValueError:
            continue

        # Freeze first row and first 3 columns
        requests.append({
            "updateSheetProperties": {
                "properties": {
                    "sheetId": sheet_id,
                    "gridProperties": {
                        "frozenRowCount": 1,
                        "frozenColumnCount": 3 if tab_name == calc_tab else 1,
                    }
                },
                "fields": "gridProperties.frozenRowCount,gridProperties.frozenColumnCount",
            }
        })

        # Header styling (row 1)
        end_col = 3 + num_periods if tab_name in [calc_tab, dashboard_tab] else 4
        requests.append({
            "repeatCell": {
                "range": {
                    "sheetId": sheet_id,
                    "startRowIndex": 0,
                    "endRowIndex": 1,
                    "startColumnIndex": 0,
                    "endColumnIndex": end_col,
                },
                "cell": {
                    "userEnteredFormat": {
                        "backgroundColor": _hex_to_color("#0f172a"),
                        "textFormat": {
                            "bold": True,
                            "foregroundColor": _hex_to_color("#ffffff"),
                        },
                    }
                },
                "fields": "userEnteredFormat(backgroundColor,textFormat)",
            }
        })

        # Date formatting: MMM d, yyyy
        if tab_name == calc_tab:
            # Period Start/End rows (rows 2-3, 0-indexed 1-2)
            requests.append({
                "repeatCell": {
                    "range": {
                        "sheetId": sheet_id,
                        "startRowIndex": 1,
                        "endRowIndex": 3,
                        "startColumnIndex": 3,
                        "endColumnIndex": 3 + num_periods,
                    },
                    "cell": {
                        "userEnteredFormat": {
                            "numberFormat": {
                                "type": "DATE",
                                "pattern": "MMM d, yyyy"
                            }
                        }
                    },
                    "fields": "userEnteredFormat.numberFormat",
                }
            })
        elif tab_name == assumptions_tab:
            # As-Of Date (column B) — use row map for robustness
            as_of_row_0 = _assumptions_row_map(planning_years)["as_of_date"] - 1
            requests.append({
                "repeatCell": {
                    "range": {
                        "sheetId": sheet_id,
                        "startRowIndex": as_of_row_0,
                        "endRowIndex": as_of_row_0 + 1,
                        "startColumnIndex": 1,
                        "endColumnIndex": 2,
                    },
                    "cell": {
                        "userEnteredFormat": {
                            "numberFormat": {
                                "type": "DATE",
                                "pattern": "MMM d, yyyy"
                            }
                        }
                    },
                    "fields": "userEnteredFormat.numberFormat",
                }
            })

    if requests:
        service.spreadsheets().batchUpdate(
            spreadsheetId=spreadsheet_id,
            body={"requests": requests}
        ).execute()


def _validate_capacity(
    service,
    spreadsheet_id: str,
    calc_tab: str,
    num_periods: int
) -> Dict[str, Any]:
    """Validate the capacity model outputs."""
    errors: List[str] = []

    try:
        # Read calc tab data — use generous row bound to cover 3-year plans
        end_col = _col_letter(3 + num_periods - 1)
        max_row = 35 + num_periods  # Scales with planning horizon
        range_str = f"{calc_tab}!A1:{end_col}{max_row}"
        resp = service.spreadsheets().values().get(
            spreadsheetId=spreadsheet_id,
            range=range_str,
            valueRenderOption="UNFORMATTED_VALUE"
        ).execute()
        values = resp.get("values", [])

        # Check for RED status in any period
        for row in values:
            if len(row) >= 3 and row[2] == "RAG Status":
                for i, val in enumerate(row[3:], start=1):
                    if val == "RED":
                        errors.append(f"Period {i}: Capacity shortfall (RED)")

    except Exception as e:
        errors.append(f"Validation error: {str(e)}")

    return {
        "status": "ok" if not errors else "issues",
        "errors": errors,
    }
