# -*- coding: utf-8 -*-
"""Transcripción del motor del Afinador a Python, para verificar la matemática
   documentada en Fermenty_Matematica_Motor.md contra el ancla de LIRONA."""
import math

# carbTotal min/max SOLO mapean el eje `intensidad` a gramos: no entran en la
# cinética ni en el backtest. El ÚNICO que entra ahí es `ref` (normaliza fS0), y
# por eso NO se toca: es el ancla contra la que se contrastaron las seis recetas
# reales. Bajar min/max con ref intacto hace que menos azúcar fermente ANTES —
# física correcta, no una regresión. Bajar `ref` a la vez congelaría los días y
# sería un parche que se compensa a sí mismo (2026-07-25: 40/100 → 30/75).
F = dict(
    carbTotal=(30,75,70), teGL=(4,8,6), arrancador=(8,22,10),
    tempViable=(16,30), tempIdeal=(20,25), tempRef=23,
    progreso=(0.30,0.88), diasF1=(5,30), volumen=(1,50), horasPor100L=12,
    factorAcido=0.12, q10=2.0, expS0=0.45, expArr=0.30, diasRef=8,
    kGasAcidez=0.18, pHSeguridad=4.2, pHIdentidad=2.8,
    gLporVolumen=4.0, abvPorGL=0.06, presionMax=8.0, residualCO2=0.5,
    tasaPorCarb=0.057, tasaPorCarbSimple=1.2, ventanaCierre=(0.8,1.2),
    margenMin=1.0, fraccionEtanolSobrevive=0.06, conversionAcetica=0.25,
)

AZUCAR = {
 'moreno':(0.97,0.92,0.06),'blanco':(1.00,1.00,0.00),'panela':(0.90,0.82,0.14),
 'miel':(0.80,1.08,0.12),'fructosa':(1.00,1.22,-0.08)}
SABOR = {
 'ninguno':(0,0,0),'hierbas':(0.03,12,0.5),'extracto':(0.01,3,0.5),
 'fresca':(0.10,40,1.0),'seca':(0.60,4,1.0),'liofilizada':(0.70,4,1.0)}

PRESETS = {
 'suave':       dict(intensidad=0.35,equilibrio=0.28,gas=0.30,complejidad=0.55,sabor='ninguno',azucar='blanco'),
 # comercial y fuerte llevaban la intensidad al tope (0,80 / 0,88) y salían a 100 y
 # 104 g/L, fuera de todo lo que el obrador ha fermentado nunca. Lo que prometen sus
 # cartas —dulzor y carácter ácido— lo dan `equilibrio` y `gas`, no el azúcar.
 'comercial':   dict(intensidad=0.62,equilibrio=0.30,gas=0.85,complejidad=0.15,sabor='liofilizada',azucar='blanco'),
 'equilibrada': dict(intensidad=0.50,equilibrio=0.50,gas=0.40,complejidad=0.60,sabor='ninguno',azucar='moreno'),
 'complejo':    dict(intensidad=0.55,equilibrio=0.55,gas=0.28,complejidad=0.90,sabor='ninguno',azucar='panela'),
 'acida':       dict(intensidad=0.50,equilibrio=0.90,gas=0.12,complejidad=0.75,sabor='ninguno',azucar='panela'),
 'fuerte':      dict(intensidad=0.60,equilibrio=0.78,gas=0.70,complejidad=0.75,sabor='fresca',azucar='panela'),
}

lerp  = lambda a,b,t: a+(b-a)*t
clamp = lambda v,a,b: min(b,max(a,v))

def formular(d, temp=23.0, volumen=3.0, dosisFruta=None):
    carbPorG, velocidad, _ = AZUCAR[d['azucar']]
    sC, sDosis, sSimple = SABOR[d['sabor']]
    dosis = 0 if d['sabor']=='ninguno' else (sDosis if dosisFruta is None else dosisFruta)
    carbFruta = dosis*sC
    carbSimpleFruta = carbFruta*sSimple

    factorEq  = lerp(1.09,0.91,d['equilibrio'])
    carbVaso  = lerp(F['carbTotal'][0],F['carbTotal'][1],d['intensidad'])*factorEq
    volObj    = lerp(0.5,4.0,d['gas'])
    carbParaGas = max(0,volObj-F['residualCO2'])*F['gLporVolumen']
    carbTotal = carbVaso+carbParaGas
    gramosAzucar = max(30,carbTotal-carbFruta)/carbPorG
    S0 = gramosAzucar*carbPorG
    te = lerp(F['teGL'][0],F['teGL'][1],d['intensidad'])

    progresoBase = lerp(F['progreso'][0],F['progreso'][1],d['equilibrio'])
    progresoObj  = clamp(progresoBase-F['kGasAcidez']*d['gas'],0.15,0.95)
    arrancador   = F['arrancador'][0]+(F['arrancador'][1]-F['arrancador'][0])*(1-d['complejidad'])**2
    pHInicial    = clamp(5.6-0.06*arrancador,3.8,5.6)

    fTemp = F['q10']**((temp-F['tempRef'])/10)
    fS0   = (S0/F['carbTotal'][2])**F['expS0']
    fArr  = (F['arrancador'][2]/arrancador)**F['expArr']
    diasCin = (F['diasRef']*(progresoObj/0.6)*fS0*fArr)/(fTemp*velocidad)
    if temp < 18: diasCin *= 1+(18-temp)*0.15
    diasCin += (volumen/100)*(F['horasPor100L']/24)

    suelo    = diasCin < F['diasF1'][0]
    diasF1   = max(diasCin,F['diasF1'][0])
    progreso = clamp(progresoObj*(F['diasF1'][0]/diasCin),0.15,0.95) if suelo else progresoObj

    residual   = S0*(1-progreso)
    carbCierre = residual+carbFruta
    fTempC = F['q10']**((temp-F['tempRef'])/10)
    tasa = (carbCierre*F['tasaPorCarb']+carbSimpleFruta*F['tasaPorCarbSimple'])*fTempC*velocidad
    alcanzable = carbParaGas <= carbCierre
    diasCierre = carbParaGas/tasa if (alcanzable and tasa>0.01) else 99

    acido = S0*progreso*F['factorAcido']
    pH = clamp(3.9-0.55*math.log(acido/2.5),2.4,4.9)
    abv = S0*progreso*F['abvPorGL']*F['fraccionEtanolSobrevive'] + \
          (carbParaGas if alcanzable else carbCierre)*F['abvPorGL']*(1-F['conversionAcetica'])

    return dict(S0=S0, te=te, arrancador=arrancador, pHInicial=pHInicial,
                progreso=progreso, suelo=suelo, diasCin=diasCin,
                ventanaF1=(max(diasF1*0.9,F['diasF1'][0]), diasF1*1.2),
                residual=residual, carbCierre=carbCierre,
                ventana2F=(diasCierre*0.8, diasCierre*1.2), alcanzable=alcanzable,
                acido=acido, pH=pH, abv=abv)

if __name__ == '__main__':
    print("=== ANCLA: equilibrada, 23 °C, 3 L ===")
    r = formular(PRESETS['equilibrada'])
    print(f"  S0 = {r['S0']:.1f} g/L   té = {r['te']:.1f} g/L   arrancador = {r['arrancador']:.1f} %")
    print(f"  pH inicial = {r['pHInicial']:.1f}")
    print(f"  días (punto) = {r['diasCin']:.1f}   ventana F1 = {r['ventanaF1'][0]:.1f}–{r['ventanaF1'][1]:.1f}")
    # El ancla del obrador (70 g/L → 7–8 días) NO se ha movido: vive en `ref` y `diasRef`,
    # intactos. Lo que se movió es DÓNDE cae la carta «equilibrada» sobre el eje: desde
    # 2026-07-25 baja a 60 g/L a propósito, así que ya no reproduce el ancla — la aligera.
    print(f"  ANCLA DEL OBRADOR (sin tocar): 70 g/L → 7–8 días")
    print(f"  ESPERADO DE LA CARTA:          60 g/L · 6 g/L té · 10 % · pH 5 · ~7 días")
    print()
    print("=== Los seis presets a 23 °C ===")
    print(f"{'preset':<13}{'S0':>7}{'arr%':>7}{'prog':>7}{'díasF1':>9}{'ventana F1':>14}{'pH':>7}{'ABV':>7}")
    for k,v in PRESETS.items():
        r = formular(v)
        w = f"{r['ventanaF1'][0]:.1f}–{r['ventanaF1'][1]:.1f}"
        s = ' ⚠suelo' if r['suelo'] else ''
        print(f"{k:<13}{r['S0']:>7.1f}{r['arrancador']:>7.1f}{r['progreso']:>7.2f}{r['diasCin']:>9.1f}{w:>14}{r['pH']:>7.2f}{r['abv']:>7.2f}{s}")
    print()
    print("=== Equilibrada por temperatura ===")
    print(f"{'°C':>5}{'díasF1':>9}{'ventana':>14}")
    for t in (18,20,22,23,25,28,30):
        r = formular(PRESETS['equilibrada'], temp=t)
        print(f"{t:>5}{r['diasCin']:>9.1f}{f'{chr(32)}{r[chr(118)+chr(101)+chr(110)+chr(116)+chr(97)+chr(110)+chr(97)+chr(70)+chr(49)][0]:.1f}–{r['ventanaF1'][1]:.1f}':>14}")
