"""AIMPACT V17: educational synthetic renewable energy model and dashboard."""
from pathlib import Path
import pandas as pd
import numpy as np
import streamlit as st
import plotly.express as px
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.model_selection import train_test_split

TITLE = 'Wind Turbine Performance Analyzer'
KIND = 'wind'
TARGET = 'power_kw'
FEATURES = ['wind_speed_m_s', 'air_density_kg_m3', 'availability', 'rated_kw']

st.set_page_config(page_title=TITLE, page_icon="⚡", layout="wide")
st.title("⚡ " + TITLE)
st.caption("AIMPACT Renewable Energy Lab • Educational demonstration • Not field-validated")
st.warning("Bundled data are synthetic and intentionally simplified. Do not use results for plant operation, dispatch, investment, or safety decisions.")
up = st.file_uploader("Optional: upload a CSV with the same feature and target column names", type=["csv"])
try:
    data = pd.read_csv(up) if up is not None else pd.read_csv(Path(__file__).with_name("sample_data.csv"))
except Exception as exc:
    st.error(f"Could not read CSV: {exc}")
    st.stop()
missing = [x for x in FEATURES + [TARGET] if x not in data.columns]
if missing:
    st.error("Missing required columns: " + ", ".join(missing))
    st.stop()
data = data[FEATURES + [TARGET]].copy()
for col in data.columns:
    data[col] = pd.to_numeric(data[col], errors="coerce")
data = data.replace([np.inf, -np.inf], np.nan).dropna()
if len(data) < 35:
    st.error("At least 35 complete numeric records are required.")
    st.stop()
st.metric("Usable records", len(data))
with st.expander("Inspect data and download the current table"):
    st.dataframe(data.head(40), use_container_width=True)
    st.download_button("Download CSV", data.to_csv(index=False), "renewable_data.csv", "text/csv")
if KIND == "hydro":
    st.subheader("Physics-based hydropower calculator")
    c1,c2,c3=st.columns(3)
    q=c1.number_input("Flow (m³/s)", min_value=0.,value=40.)
    h=c2.number_input("Net head (m)", min_value=0.,value=50.)
    eta=c3.slider("Turbine-generator efficiency",0.0,1.0,.88,.01)
    st.metric("Estimated generation", f"{1000*9.81*q*h*eta/1e6:,.3f} MW")
    st.caption("P = ρgQHη; this simplified calculation excludes detailed hydraulic and plant losses.")
if KIND == "hybrid":
    st.subheader("Simplified battery dispatch")
    c1,c2,c3=st.columns(3)
    capacity=c1.number_input("Battery energy capacity (kWh)",min_value=0.,value=1000.,step=100.)
    power=c2.number_input("Battery max charge/discharge (kW)",min_value=0.,value=300.,step=50.)
    initial=c3.slider("Initial battery state of charge (%)",0,100,50)
    soc=capacity*initial/100
    logs=[]
    # Simulated chronological 1-hour periods, charge/discharge efficiency 90% each way.
    for _,row in data.sort_values("hour").head(24).iterrows():
        surplus=row['solar_kw']+row['wind_kw']-row['demand_kw']
        if surplus>=0:
            charged=min(surplus,power,(capacity-soc)/.9 if capacity>soc else 0)
            soc+=charged*.9;unserved=0;curtail=surplus-charged
        else:
            discharged=min(-surplus,power,soc*.9)
            soc-=discharged/.9;unserved=-surplus-discharged;curtail=0
        logs.append(dict(hour=int(row['hour']),soc_pct=(soc/capacity*100 if capacity else 0),unserved_kwh=unserved,curtailed_kwh=curtail))
    dispatch=pd.DataFrame(logs)
    st.plotly_chart(px.line(dispatch,x='hour',y='soc_pct',title='Battery state of charge — illustrative 24-hour dispatch'),use_container_width=True)
    st.metric("Unserved energy in simulated day",f"{dispatch.unserved_kwh.sum():,.1f} kWh")
    st.metric("Curtailed energy in simulated day",f"{dispatch.curtailed_kwh.sum():,.1f} kWh")
    st.caption("Illustrative 1-hour dispatch; no degradation, reserve constraints, network limits or tariff optimization.")
st.subheader("Train an introductory ML baseline")
st.caption("Random 80/20 holdout on synthetic tabular data. Time-dependent real datasets need chronological validation to avoid leakage.")
X_train,X_test,y_train,y_test=train_test_split(data[FEATURES],data[TARGET],test_size=.2,random_state=42)
model=RandomForestRegressor(n_estimators=90,min_samples_leaf=2,random_state=42,n_jobs=-1)
model.fit(X_train,y_train)
pred=model.predict(X_test)
mae=mean_absolute_error(y_test,pred);rmse=float(np.sqrt(mean_squared_error(y_test,pred)));r2=r2_score(y_test,pred)
c1,c2,c3=st.columns(3)
c1.metric("MAE",f"{mae:,.2f}");c2.metric("RMSE",f"{rmse:,.2f}");c3.metric("R²",f"{r2:,.3f}")
fig=px.scatter(x=y_test,y=pred,labels={'x':'Actual','y':'Predicted'},title='Holdout predictions vs actual values')
st.plotly_chart(fig,use_container_width=True)
importance=pd.DataFrame({'Feature':FEATURES,'Importance':model.feature_importances_}).sort_values('Importance',ascending=False)
st.plotly_chart(px.bar(importance,x='Feature',y='Importance',title='Random Forest feature importance'),use_container_width=True)
st.subheader("Try a single prediction")
cols=st.columns(min(len(FEATURES),4));inputs={}
for i,col in enumerate(FEATURES):
    inputs[col]=cols[i%len(cols)].number_input(col,value=float(data[col].median()),format='%.3f')
if st.button("Predict",type="primary"):
    x=pd.DataFrame([inputs])[FEATURES]
    st.success(f"Illustrative predicted {TARGET}: {model.predict(x)[0]:,.2f}")
st.info("For real projects: check measurement quality, units, site transferability, leakage, seasonality, uncertainty, and operational validation before using any model.")
