"""Render website figures from reproduce.py's aggregate results; requires matplotlib."""
from pathlib import Path
import json
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.colors import TwoSlopeNorm,LinearSegmentedColormap
from matplotlib.ticker import PercentFormatter,FuncFormatter
ROOT=Path(__file__).resolve().parents[2]
plt.rcParams.update({'font.family':'sans-serif','font.sans-serif':['Arial','DejaVu Sans'],'svg.fonttype':'none','svg.hashsalt':'kelun-analytics','font.size':11})

def setup(theme,title,subtitle,figsize=(9,5.8),left=.12):
    bg,ink,muted,line=('#fffefa','#111214','#626760','#d9d9d3') if theme=='light' else ('#1a1e21','#f3efe5','#b4bcb7','#414a4e')
    fig,ax=plt.subplots(figsize=figsize,facecolor=bg);ax.set_facecolor(bg)
    fig.subplots_adjust(left=left,right=.96,bottom=.16,top=.76)
    fig.text(left,.92,title,fontfamily='Georgia',fontsize=20,color=ink)
    fig.text(left,.84,subtitle,fontsize=10.5,color=muted)
    for s in ax.spines.values():s.set_visible(False)
    ax.tick_params(colors=muted,length=0,pad=8)
    ax.xaxis.label.set_color(ink);ax.yaxis.label.set_color(ink)
    return fig,ax,{'bg':bg,'ink':ink,'muted':muted,'line':line,'blue':'#4f8fe8' if theme=='light' else '#82b6ff','coral':'#df846b' if theme=='light' else '#ff927c','mint':'#54ad82' if theme=='light' else '#78d6a2'}

def finish(fig,slug,name,theme):
    fig.savefig(ROOT/'projects'/slug/'assets'/f'{name}-{theme}.svg',metadata={'Date':None})
    plt.close(fig)

for theme in ['light','dark']:
    slug='s-mobile-churn';r=json.loads((ROOT/'projects'/slug/'assets/results.json').read_text())
    fig,ax,c=setup(theme,'Better ranking on unseen customers','Test sample: 11,700 customers · same holdout for both models')
    for name,key,color in [('Logistic','logistic_test',c['coral']),('Gradient boosting','boosting_test',c['blue'])]:
        v=r['roc'][name];ax.plot(v['fpr'],v['tpr'],color=color,lw=2.3,label=f'{name} · AUC {r["auc"][key]:.3f}')
    ax.plot([0,1],[0,1],color=c['line'],linestyle='--')
    ax.set(xlim=(0,1),ylim=(0,1),xlabel='False positive rate',ylabel='True positive rate')
    ax.grid(color=c['line'],alpha=.5);ax.legend(frameon=False,labelcolor=c['ink'],loc='lower right')
    finish(fig,slug,'model-ranking',theme)

    d=r['deciles'];fig,ax,c=setup(theme,'Useful ranking, imperfect calibration','Independent representative sample: 30,000 customers · observed churn 2.00%')
    x=np.arange(1,11);obs=[v['observed'] for v in d];pred=[v['predicted'] for v in d]
    ax.bar(x,obs,color=c['blue'],alpha=.75,width=.65,label='Observed churn')
    ax.plot(x,pred,'o-',color=c['coral'],lw=2,label='Prior-corrected prediction')
    ax.axhline(.02,color=c['muted'],linestyle='--',lw=1,label='Population average')
    ax.set(xticks=x,xlabel='Risk decile · 1 = highest predicted risk',ylim=(0,.115))
    ax.yaxis.set_major_formatter(PercentFormatter(1,decimals=0));ax.grid(axis='y',color=c['line'],alpha=.5);ax.set_axisbelow(True)
    ax.annotate('Top 10% contains\n41.3% of observed churners',xy=(1,.0827),xytext=(2.5,.093),color=c['ink'],fontsize=11,arrowprops={'arrowstyle':'-','color':c['muted']})
    ax.legend(frameon=False,labelcolor=c['ink'],loc='upper right',fontsize=9)
    finish(fig,slug,'risk-deciles',theme)

    names={'eqpdays':'Handset age','overage':'Overage minutes','months':'Customer tenure','occupation':'Occupation','mou':'Minutes of use','revenue':'Monthly revenue'}
    imp=r['importance'][:6][::-1];fig,ax,c=setup(theme,'Which inputs carry the predictive signal?','Permutation importance · test AUC decrease · 3 repeats',left=.25)
    ax.barh([names.get(v['feature'],v['feature']) for v in imp],[v['mean'] for v in imp],xerr=[v['std'] for v in imp],color=c['blue'],height=.6,error_kw={'ecolor':c['muted'],'capsize':3})
    ax.set_xlabel('Decrease in AUC after shuffling one feature');ax.grid(axis='x',color=c['line'],alpha=.5);ax.set_axisbelow(True)
    # Long title starts at figure margin, independent of the label-heavy chart.
    fig.texts[0].set_x(.07);fig.texts[1].set_x(.07)
    finish(fig,slug,'risk-drivers',theme)

    slug='fitech-offer-design';r=json.loads((ROOT/'projects'/slug/'assets/results.json').read_text())
    fig,ax,c=setup(theme,'The best offer depends on the segment','Expected CLV × response probability − $0.50 contact cost · per prospect',figsize=(9,7.9),left=.34)
    scores=r['scores'];matrix=np.array([[next(v['margin_per_email'] for v in scores if v['offer']==o and v['bk_score']==bk) for bk in [150,200,250]] for o in range(1,13)])
    cmap=LinearSegmentedColormap.from_list('margin',[c['coral'],c['bg'],c['blue']]);norm=TwoSlopeNorm(vmin=-.4,vcenter=0,vmax=2.4)
    ax.imshow(matrix,cmap=cmap,norm=norm,aspect='auto')
    labels=[]
    for o in range(1,13):
        row=next(v for v in scores if v['offer']==o)
        labels.append(f'{o:02}   {row["apr"]:.1f}% · {row["fixed_var"][:3]} · ${row["annual_fee"]}')
    ax.set_yticks(range(12),labels);ax.set_xticks(range(3),['BK 150','BK 200','BK 250']);ax.xaxis.tick_top()
    for i in range(12):
        for j in range(3):ax.text(j,i,f'${matrix[i,j]:.2f}',ha='center',va='center',color=c['ink'],fontsize=11)
    for j,bk in enumerate([150,200,250]):
        i=r['best']['pick'][str(bk)]-1
        ax.add_patch(Rectangle((j-.48,i-.46),.96,.92,fill=False,edgecolor=c['ink'],lw=2))
    fig.texts[0].set_x(.07);fig.texts[1].set_x(.07)
    fig.text(.07,.055,'Row labels: offer · APR · rate type · annual fee. Outlined cells: selected allocation.',color=c['muted'],fontsize=10)
    finish(fig,slug,'offer-economics',theme)

    fig,ax,c=setup(theme,'From response forecasts to campaign value','750,000 prospects · 250,000 per BK group · static model projection')
    values=[r['gross_expected_clv'],-r['contact_cost'],-r['best']['design_and_round_cost'],r['best']['net']]
    bottoms=[0,values[0]+values[1],values[0]+values[1]+values[2],0]
    colors=[c['blue'],c['coral'],c['coral'],c['mint']]
    bars=ax.bar(range(4),[abs(v)/1e6 for v in values],bottom=np.array(bottoms)/1e6,color=colors,width=.6)
    for i,b in enumerate(bars):
        top=b.get_y()+b.get_height()
        label=f'${abs(values[i])/1e6:.3f}M' if i!=2 else '$12.8K'
        ax.text(i,top+.04,('−' if values[i]<0 else '')+label,ha='center',color=c['ink'],fontsize=11)
    ax.set_xticks(range(4),['Expected\ncustomer value','Contact\ncost','Design +\nround cost','Expected\nnet value'])
    ax.set_ylim(0,1.8);ax.yaxis.set_major_formatter(FuncFormatter(lambda v,pos:f'${v:.1f}M'))
    ax.grid(axis='y',color=c['line'],alpha=.5);ax.set_axisbelow(True)
    finish(fig,slug,'campaign-value',theme)

    slug='pentathlon-next-product';r=json.loads((ROOT/'projects'/slug/'assets/results.json').read_text())
    fig,ax,c=setup(theme,'Choose the message by expected contribution','180,000 held-out customer profiles · 40% margin · before contact costs',left=.24)
    names=['No message','Random assignment','Best uniform','Response targeting','Value targeting']
    vals=[r['strategies'][n] for n in names]
    ax.barh(names,vals,color=[c['line'],c['line'],c['coral'],c['coral'],c['blue']],height=.58)
    ax.invert_yaxis();ax.set_xlim(0,.85)
    for i,v in enumerate(vals):ax.text(v+.015,i,f'€{v:.3f}',va='center',color=c['ink'])
    ax.set_xlabel('Model-implied gross contribution per customer (€)');ax.grid(axis='x',color=c['line'],alpha=.5);ax.set_axisbelow(True)
    fig.texts[0].set_x(.07);fig.texts[1].set_x(.07)
    finish(fig,slug,'policy-value',theme)

    fig,ax,c=setup(theme,'The objective changes the recommendation','Share of held-out customers assigned to each option',left=.21)
    order=['endurance','strength','racquet','backcountry','team','water','control']
    lookup={v['message']:v for v in r['allocation']};y=np.arange(7)
    ax.barh(y-.18,[lookup[m]['response_share'] for m in order],height=.32,color=c['coral'],label='Maximize response')
    ax.barh(y+.18,[lookup[m]['value_share'] for m in order],height=.32,color=c['blue'],label='Maximize value')
    ax.set_yticks(y,[m.title() if m!='control' else 'No message' for m in order]);ax.invert_yaxis()
    ax.set_xlim(0,.83);ax.xaxis.set_major_formatter(PercentFormatter(1));ax.legend(frameon=False,labelcolor=c['ink'],loc='lower right')
    ax.grid(axis='x',color=c['line'],alpha=.5);ax.set_axisbelow(True)
    fig.texts[0].set_x(.07);fig.texts[1].set_x(.07)
    finish(fig,slug,'message-allocation',theme)
print('Rendered 14 SVG charts from reproduced results.')
