"""Modelos reproducibles: IPC nacional INDEC; evaluación temporal y pronósticos.
Ejecutar: pip install -r requirements.txt && python modelos_arima_sarima.py
"""
from pathlib import Path
import json
import numpy as np
import pandas as pd
import requests
from statsmodels.tsa.statespace.sarimax import SARIMAX

OUT=Path(__file__).resolve().parent
URL='https://apis.datos.gob.ar/series/api/series'
SERIES_ID='148.3_INIVELNAL_DICI_M_26'
r=requests.get(URL,params={'ids':SERIES_ID,'limit':5000},timeout=45)
r.raise_for_status()
data=r.json()['data']
df=pd.DataFrame(data,columns=['fecha','ipc'])
df['fecha']=pd.to_datetime(df['fecha'])
df['ipc']=pd.to_numeric(df['ipc'],errors='coerce')
df=df.dropna().drop_duplicates('fecha').sort_values('fecha').set_index('fecha')
df.index=df.index.to_period('M').to_timestamp()
df=df.asfreq('MS')
if df['ipc'].isna().any() or (df['ipc']<=0).any(): raise ValueError('Serie con valores inválidos o meses faltantes')
df['inflacion_mensual_pct']=df['ipc'].pct_change()*100
df['inflacion_interanual_pct']=df['ipc'].pct_change(12)*100
y=df['inflacion_mensual_pct'].dropna()
if len(y)<60: raise ValueError('Se requieren al menos 60 variaciones mensuales')
# Holdout final 12 meses. Selección exclusivamente por MAE fuera de muestra.
train,test=y.iloc[:-12],y.iloc[-12:]
candidates=[('Naive',None,None),('ARIMA(1,0,0)',(1,0,0),(0,0,0,0)),('ARIMA(2,0,0)',(2,0,0),(0,0,0,0)),('ARIMA(1,1,1)',(1,1,1),(0,0,0,0)),('SARIMA(1,0,0)x(1,0,0,12)',(1,0,0),(1,0,0,12)),('SARIMA(1,0,1)x(1,0,0,12)',(1,0,1),(1,0,0,12))]
results=[]
for name,order,seasonal in candidates:
 try:
  if order is None: pred=np.repeat(train.iloc[-1],len(test))
  else:
   fit=SARIMAX(train,order=order,seasonal_order=seasonal,trend='c',enforce_stationarity=False,enforce_invertibility=False).fit(disp=False,maxiter=250)
   pred=fit.forecast(len(test)).to_numpy()
  err=test.to_numpy()-pred
  results.append({'modelo':name,'mae':float(np.mean(np.abs(err))),'rmse':float(np.sqrt(np.mean(err**2))),'orden':order,'estacional':seasonal})
 except Exception as exc: print('No convergió',name,str(exc)[:140])
if not results: raise RuntimeError('Ningún modelo pudo evaluarse')
results.sort(key=lambda x:x['mae'])
best=results[0]
if best['orden'] is None:
 forecast=np.repeat(y.iloc[-1],12); lo=[None]*12;hi=[None]*12
else:
 fit=SARIMAX(y,order=best['orden'],seasonal_order=best['estacional'],trend='c',enforce_stationarity=False,enforce_invertibility=False).fit(disp=False,maxiter=250)
 f=fit.get_forecast(12);forecast=f.predicted_mean.to_numpy();ci=f.conf_int(alpha=.05);lo=ci.iloc[:,0].to_list();hi=ci.iloc[:,1].to_list()
future=pd.date_range(y.index[-1]+pd.offsets.MonthBegin(1),periods=12,freq='MS')
predictions=pd.DataFrame({'fecha':future,'inflacion_mensual_pronosticada_pct':forecast,'ic95_inferior':lo,'ic95_superior':hi})
metrics=pd.DataFrame([{k:v for k,v in row.items() if k not in ('orden','estacional')} for row in results])
df.to_csv(OUT/'ipc_procesado.csv',index_label='fecha')
metrics.to_csv(OUT/'comparacion_modelos.csv',index=False)
predictions.to_csv(OUT/'pronosticos_12_meses.csv',index=False)
(OUT/'resumen_modelo.json').write_text(json.dumps({'fuente':'INDEC via Datos Argentina','id_serie':SERIES_ID,'ultimo_mes':str(y.index[-1].date()),'holdout_meses':12,'modelo_elegido':best['modelo'],'mae_holdout':best['mae'],'rmse_holdout':best['rmse'],'nota':'La selección se realiza sobre un único bloque final de 12 meses; los intervalos son condicionales al modelo y no incorporan incertidumbre de selección.'},ensure_ascii=False,indent=2),encoding='utf-8')
print(metrics.to_string(index=False));print('\nMejor modelo:',best['modelo']);print(predictions.to_string(index=False))
