const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export interface Scenario { id: string; name: string; status: string; kpis_json: string | null; created_at: string; runtime_s?: number | null; } export interface ScenarioDetail extends Scenario { inputs_json: string | null; statements_json: string | null; debt_schedule_json: string | null; error_message: string | null; } export interface KpiSummary { solved_tariff_inr_per_kwh?: number | null; equity_irr?: number | null; project_irr?: number | null; min_dscr?: number | null; avg_dscr?: number | null; total_capex_cr?: number | null; idc_cr?: number | null; debt_cr?: number | null; solar_y1_cuf?: number | null; wind_y1_plf?: number | null; lcoe_inr_per_kwh?: number | null; payback_years?: number | null; rtc_cuf_achieved?: number | null; total_shortfall_mwh?: number | null; total_curtailed_mwh?: number | null; total_mcp_revenue_cr?: number | null; } export interface PnLRow { year: number; revenue_cr: number; ppa_revenue_cr: number; mcp_revenue_cr: number; ppa_tariff_inr_per_kwh: number; ppa_units_mwh: number; mcp_units_mwh: number; opex_total_cr: number; om_cr: number; insurance_cr: number; land_lease_cr: number; am_fee_cr: number; misc_opex_cr: number; ebitda_cr: number; depreciation_book_cr: number; ebit_cr: number; interest_cr: number; pbt_cr: number; tax_cr: number; pat_cr: number; } export interface CfsRow { year: number; pat_cr: number; depreciation_cr: number; delta_working_capital_cr: number; cfo_cr: number; capex_cr: number; cfi_cr: number; debt_drawdown_cr: number; debt_repayment_cr: number; equity_injection_cr: number; cff_cr: number; net_cash_flow_cr: number; opening_cash_cr: number; closing_cash_cr: number; } export interface BsRow { year: number; gross_block_cr: number; accumulated_depr_cr: number; net_block_cr: number; cash_cr: number; receivables_cr: number; total_assets_cr: number; equity_cr: number; reserves_cr: number; long_term_debt_cr: number; payables_cr: number; total_liabilities_cr: number; } export interface DebtYearRow { year: number; opening_balance_cr: number; interest_cr: number; principal_cr: number; total_debt_service_cr: number; closing_balance_cr: number; dscr: number; } export interface GenerationRow { year: number; solar_mwh: number; wind_mwh: number; gross_mwh: number; aux_loss_mwh: number; tx_loss_mwh: number; dsm_loss_mwh: number; net_billable_mwh: number; solar_cuf_pct: number | null; wind_plf_pct: number | null; revenue_cr: number; } export interface IdcMonthRow { month: number; equity_draw_cr: number; debt_draw_cr: number; idc_accrual_cr: number; cum_equity_cr: number; cum_debt_cr: number; cum_idc_cr: number; cum_tpc_cr: number; } export interface IdcPhasing { construction_months: number; base_capex_cr: number; idc_cr: number; total_capex_cr: number; debt_cr: number; equity_cr: number; monthly: IdcMonthRow[]; } export interface Statements { pnl: PnLRow[]; cfs: CfsRow[]; bs: BsRow[]; generation?: GenerationRow[]; idc_phasing?: IdcPhasing; // Hourly generation: 25 years × 8760 hours = 219,000 values per technology solar_hourly?: number[]; wind_hourly?: number[]; // Hourly timeseries hourly_timestamps?: string[]; hourly_fy?: string[]; hourly_proj_year?: number[]; hourly_total_re?: number[]; hourly_client_end?: number[]; hourly_load?: number[]; // Profile data for detailed view hourly_solar_profile?: number[]; hourly_wind_profile?: number[]; } export type CostBasis = | "PER_WP_DC" | "PER_MWP_DC" | "PER_MW_AC" | "PER_MW_WIND" | "PER_MWH_BESS" | "PER_ACRE" | "PCT_OF_HARDCOST" | "ABS_INR_CR"; export type DeprClass = | "Plant" | "BESS" | "Building" | "Land_NoDepr" | "LandLease_Amortized" | "Intangible" | "Capitalized_NoDepr" | "Expensed"; export type CostAttribution = "SolarOnly" | "WindOnly" | "BESSOnly" | "Common"; export interface CostItem { id: string; name: string; category: "HardCost" | "SoftCost" | "EPCOverhead" | "EPCMargin" | "FinancingCost" | "Contingency"; basis: CostBasis; value: number; depr_class: DeprClass; tax_pct?: number; // GST/tax rate, default 5% for modules attribution: CostAttribution; phasing_id?: string; escalation_pct?: number; } export interface ScenarioInputPayload { project?: { name?: string; state?: string | null; capacity_solar_mwp?: number; capacity_wind_mw?: number; capacity_bess_mwh?: number; capacity_bess_mw?: number; land_acres?: number; cod_year?: number; cod_date?: string | null; solar_cod_date?: string | null; wind_cod_date?: string | null; bess_cod_date?: string | null; }; solar?: { location_id: string; capacity_dc_mwp: number; capacity_ac_mw: number; dc_ac_ratio?: number; availability_fraction?: number; dc_loss_fraction?: number; soiling_fraction?: number; degradation_y1?: number; degradation_annual?: number; stabilization_days?: number; stabilization_energy_loss_frac?: number; stabilization_dsm_addon_pct?: number; } | null; wind?: { location_id: string; capacity_mw: number; hub_height_m?: number; availability_fraction?: number; wake_loss_fraction?: number; stabilization_days?: number; stabilization_energy_loss_frac?: number; stabilization_dsm_addon_pct?: number; } | null; bess?: { capacity_mwh: number; power_mw: number; rte?: number; dod?: number; } | null; rtc?: { rtc_mw?: number; mcp_enabled?: boolean; initial_soc_frac?: number; } | null; commercial?: { tariff_inr_per_kwh?: number; aux_consumption_pct?: number; transmission_loss_pct?: number; dsm_loss_pct?: number; bad_debt_pct?: number; receivable_days?: number; payable_days?: number; }; opex?: { om_solar_cr_per_mw?: number; om_wind_cr_per_mw?: number; om_bess_cr_per_mwh?: number; insurance_pct_of_capex?: number; land_lease_cr?: number; om_escalation_pct?: number; om_solar_escalation_pct?: number; om_solar_escalation_after_year?: number; om_wind_escalation_pct?: number; om_wind_escalation_after_year?: number; om_bess_pct_of_capex?: number | null; am_fee_pct_of_revenue?: number; misc_cr?: number; }; capex?: { cost_items?: CostItem[]; debt_fraction?: number; interest_rate_annual?: number; construction_months?: number; upfront_fee_pct?: number; }; debt?: { interest_rate_annual?: number; tenor_years?: number; moratorium_years?: number; de_ratio?: number; min_dscr?: number; avg_dscr?: number; schedule_shape?: string; }; tax?: { rate?: number; wdv_plant_rate?: number; wdv_bess_rate?: number; wdv_building_rate?: number; wdv_intangible_rate?: number; }; solver?: { mode: "solve_tariff" | "fixed_tariff"; target_equity_irr?: number; fixed_tariff?: number | null; }; } export interface ProgressEvent { stage: string; pct: number; } // --------------------------------------------------------------------------- // API helpers // --------------------------------------------------------------------------- async function apiFetch(url: string, options?: RequestInit): Promise { const res = await fetch(`${API_BASE}${url}`, options); if (!res.ok) throw new Error(`API error ${res.status}: ${await res.text()}`); return res.json() as Promise; } // --------------------------------------------------------------------------- // Scenario functions // --------------------------------------------------------------------------- export async function createScenario( name: string, inputs?: ScenarioInputPayload, ): Promise { return apiFetch("/api/scenarios", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, inputs }), }); } export async function getScenario(id: string): Promise { return apiFetch(`/api/scenarios/${id}`); } export async function listScenarios(): Promise { return apiFetch("/api/scenarios"); } export async function getKpis(id: string): Promise { return apiFetch(`/api/scenarios/${id}/kpis`); } export async function getStatements(id: string): Promise { return apiFetch(`/api/scenarios/${id}/statements`); } export async function archiveScenario(id: string): Promise { await apiFetch(`/api/scenarios/${id}`, { method: "DELETE" }); } export async function updateScenarioInputs( id: string, inputs: ScenarioInputPayload, ): Promise { return apiFetch(`/api/scenarios/${id}/inputs`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ inputs }), }); } export function scenarioEventsUrl(id: string): string { return `${API_BASE}/api/scenarios/${id}/events`; } export function scenarioExcelUrl(id: string): string { return `${API_BASE}/api/scenarios/${id}/export/excel`; } // --------------------------------------------------------------------------- // Profile functions // --------------------------------------------------------------------------- export interface ProfileInfo { id: string; name: string; path: string; } export interface ProfileStats { kind: string; location_id: string; count: number; avg: number; std: number; min: number; max: number; median: number; p25: number; p75: number; p90: number; p95: number; cuf_pct: number | null; } export interface ProfileData { location_id: string; values: number[]; count: number; } export async function getAvailableProfiles(): Promise<{ solar: ProfileInfo[]; wind: ProfileInfo[] }> { return apiFetch<{ solar: ProfileInfo[]; wind: ProfileInfo[] }>("/api/profiles/available"); } export async function getProfileStats(kind: string, locationId: string): Promise { return apiFetch(`/api/profiles/stats/${kind}/${locationId}`); } export async function getSolarProfile(locationId: string): Promise { return apiFetch(`/api/profiles/solar/${locationId}`); } export async function getWindProfile(locationId: string): Promise { return apiFetch(`/api/profiles/wind/${locationId}`); } export function profileCsvUrl(kind: string, locationId: string): string { return `${API_BASE}/api/profiles/${kind}/${locationId}?as_csv=true`; }