from pathlib import Path
import math
import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter
R=Path(__file__).resolve().parent
BLUE='#287ba5';ORANGE='#d56b43';INK='#253342';GRAY='#83919b'
plt.rcParams.update({'font.family':'DejaVu Sans','font.size':11,'text.color':INK,'text.parse_math':False,
    'axes.labelcolor':INK,'xtick.color':INK,'ytick.color':INK,'axes.spines.top':False,
    'axes.spines.right':False,'axes.spines.left':False,'axes.edgecolor':'#d5dde3',
    'figure.facecolor':'#fafbfc','axes.facecolor':'#fafbfc','savefig.facecolor':'#fafbfc'})

g=pd.read_csv(R/'growth_fractional_genres.csv').pivot(index='family',columns='year',values='fractional_games')
order=['none_of_four','action','deck','tower','auto']
names=['Outside these four tags','Action roguelike','Roguelike deckbuilder','Tower defense','Auto battler']
fig,axs=plt.subplots(1,2,figsize=(15,6.3),gridspec_kw={'width_ratios':[1.2,1]})
fig.subplots_adjust(top=.79,bottom=.23,wspace=.42,left=.20,right=.95)
fig.suptitle('The growth in build promises is broad — and concentrated at lower prices',x=.05,ha='left',y=.98,fontsize=17,fontweight='bold')
fig.text(.05,.91,'January–August releases in each year • paid, non-explicit, focal exclusions • September 5, 2026 snapshot',fontsize=11)
ys=np.arange(5)
for i,(year,color) in enumerate([(2025,BLUE),(2026,ORANGE)]):
    vals=g.loc[order,year].to_numpy()
    axs[0].barh(ys+(i-.5)*.32,vals,height=.29,color=color,label=str(year))
    for y,v in zip(ys+(i-.5)*.32,vals):axs[0].text(v+9,y,f'{v:,.1f}',va='center',fontsize=10)
axs[0].set_yticks(ys,names);axs[0].invert_yaxis();axs[0].set_xlim(0,930)
axs[0].set_title('Build-promising games by genre',loc='left',fontweight='bold',pad=16)
axs[0].set_xlabel('Fractional game count; overlaps split equally')
axs[0].legend(frameon=False,loc='lower right')
p=pd.read_csv(R/'growth_price_text.csv').query("axis == 'price_bucket'").pivot(index='value',columns='year',values='builds')
order2=['<=5','5-10','10-20','20-40','>40']
delta=(p[2026]-p[2025]).loc[order2].to_numpy()
axs[1].barh(np.arange(5),delta,color=[ORANGE if v>0 else GRAY for v in delta],height=.55)
axs[1].set_yticks(np.arange(5),['$5 or less','Over $5–$10','Over $10–$20','Over $20–$40','Over $40'])
axs[1].invert_yaxis();axs[1].axvline(0,color='#aab7c2',lw=.8)
for y,v in enumerate(delta):axs[1].text(v+7 if v>=0 else v-7,y,f'{v:+,.0f}',ha='left' if v>=0 else 'right',va='center')
axs[1].set_xlim(-45,470);axs[1].set_xlabel('Change in number of build-promising games')
axs[1].set_title('Additional games by current US price',loc='left',fontweight='bold',pad=16)
fig.text(.05,.09,'843 → 1,717 games (+103.7%). $10-and-under tiers account for 715 of the 874 additional games (81.8%).',fontsize=12,fontweight='bold')
fig.text(.05,.035,'Current prices are not launch prices or production budgets. Missing-price games account for −1 of the change. Genre bars add to the totals.',fontsize=10)
for ext in ['png','svg']:fig.savefig(R/f'growth.{ext}',dpi=160)
plt.close(fig)

def wilson(k,n):
    z=1.96;p=k/n;c=(p+z*z/(2*n))/(1+z*z/n);h=z*math.sqrt(p*(1-p)/n+z*z/(4*n*n))/(1+z*z/n)
    return c-h,c+h
x=pd.read_csv(R/'reception_groups.csv')
fig,axs=plt.subplots(1,3,figsize=(16,6.7),sharey=True)
fig.subplots_adjust(left=.12,right=.97,top=.73,bottom=.26,wspace=.19)
fig.suptitle('Deckbuilders: reaching an audience and satisfying it are different results',x=.045,ha='left',y=.98,fontsize=17,fontweight='bold')
fig.text(.045,.915,'Deck-tagged strict builds vs other strict builds • raw proportions, with exact counts and descriptive 95% Wilson intervals',fontsize=11)
periods=['2025','2026_H1','2026_JulAug']
metrics=[('ge100','n','At least 100 reviews'),('liked100','ge100','At least 80% positive\namong games with 100+ reviews'),('liked100','n','Both: 100+ reviews\nand at least 80% positive')]
for ax,(num,den,title) in zip(axs,metrics):
    for i,p in enumerate(periods):
        for group,color,offset in [('deck_strict',BLUE,-.14),('other_builds',ORANGE,.14)]:
            r=x[x.period.eq(p)&x.group.eq(group)].iloc[0];k=int(r[num]);n=int(r[den]);v=k/n;lo,hi=wilson(k,n)
            ax.errorbar(v,i+offset,xerr=[[v-lo],[hi-v]],fmt='o',color=color,capsize=3,ms=6,lw=1.6)
            ax.text(min(hi+.025,1.02),i+offset,f'{k}/{n} ({v:.1%})',va='center',fontsize=9,color=color)
    ax.set_title(title,loc='left',fontweight='bold',pad=17)
    ax.set_xlim(0,1.4 if den=='ge100' else .70);ax.set_xticks([0,.25,.5,.75,1] if den=='ge100' else [0,.2,.4,.6])
    ax.xaxis.set_major_formatter(PercentFormatter(1));ax.grid(axis='x',alpha=.15)
axs[0].set_yticks(range(3),['2025','Jan–Jun 2026','Jul–Aug 2026']);axs[0].invert_yaxis()
fig.text(.045,.155,'● Deck-tagged builds',color=BLUE,fontsize=12,fontweight='bold')
fig.text(.25,.155,'● Other builds',color=ORANGE,fontsize=12,fontweight='bold')
fig.text(.045,.09,'Compare groups within a row. Newer cohorts have had less time to accumulate reviews; this chart does not measure deterioration over time.',fontsize=11)
fig.text(.045,.035,'Intervals describe count uncertainty under a simple binomial model; they do not account for exploratory selection or creator dependence.',fontsize=10)
for ext in ['png','svg']:fig.savefig(R/f'deck_reception.{ext}',dpi=160)
plt.close(fig)
print('Saved growth and deck reception figures as PNG and SVG.')
