# /// script
# requires-python = ">=3.12"
# dependencies = ["matplotlib==3.10.8"]
# ///
"""Reproduce the cash-flow bridge. Save microsoft-cashflow.json beside this file.
Run: uv run cashflow.py (Python and matplotlib dependencies declared above).
Inputs are USD millions from Microsoft's FY26 10-K.
"""
from pathlib import Path
import json
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

base = Path(__file__).resolve().parent
records = json.loads((base / 'microsoft-cashflow.json').read_text())['observations']
prior, current = records[-2:]
start = (prior['operating_cash'] - prior['property_equipment_additions']) / 1000
cash_change = (current['operating_cash'] - prior['operating_cash']) / 1000
spend_change = (current['property_equipment_additions'] - prior['property_equipment_additions']) / 1000
end = (current['operating_cash'] - current['property_equipment_additions']) / 1000
assert abs(start + cash_change - spend_change - end) < 1e-10
metrics = {
    'start_billions': start, 'additional_operating_cash_billions': cash_change,
    'additional_property_equipment_spend_billions': spend_change, 'end_billions': end,
    'operating_cash_growth': current['operating_cash']/prior['operating_cash']-1,
    'property_equipment_spend_growth': current['property_equipment_additions']/prior['property_equipment_additions']-1,
    'residual_change': end/start-1,
    'capex_share_of_operating_cash': {r['year']: r['property_equipment_additions']/r['operating_cash'] for r in records},
}
(base / 'cashflow-results.json').write_text(json.dumps(metrics, indent=2)+'\n')
plt.rcParams.update({'font.family':'DejaVu Sans','font.size':12,'axes.spines.top':False,'axes.spines.right':False,'axes.spines.left':False,'axes.spines.bottom':False,'figure.facecolor':'white','axes.facecolor':'white','text.color':'#182739','xtick.color':'#526174','ytick.color':'#526174','svg.fonttype':'path'})
for compact in (False, True):
    fig, ax = plt.subplots(figsize=(4.5,3.8) if compact else (9,3.9), layout='constrained')
    ax.bar(range(4), [start, cash_change, spend_change, end], bottom=[0,start,end,0], color=['#234d7b','#718ca5','#ac703f','#234d7b'],width=.58)
    for x, y in [(0,start),(1,start+cash_change),(2,end)]:
        ax.plot([x+.29,x+1-.29],[y,y],color='#aab5c0',lw=.8,ls=(0,(2,2)))
    for x, y, label in [(0,start,f'${start:.1f}B'),(1,start+cash_change,f'+${cash_change:.1f}B'),(2,start+cash_change,f'−${spend_change:.1f}B'),(3,end,f'${end:.1f}B')]:
        ax.text(x,y+4,label,ha='center',va='bottom',fontsize=10 if compact else 13,fontweight='medium')
    ax.set_xticks(range(4),['FY25\nremaining','More cash\nfrom operations','More cash\nto PP&E','FY26\nremaining'] if compact else ['FY25 remaining','Operating cash increase','PP&E spending increase','FY26 remaining'])
    ax.set_ylim(0,145)
    ax.set_yticks([0,40,80,120])
    ax.yaxis.set_major_formatter(FuncFormatter(lambda x,pos:f'${x:.0f}B'))
    ax.tick_params(axis='both',length=0,pad=9,labelsize=9 if compact else 11)
    ax.grid(axis='y',color='#e4e8ec',lw=.7)
    ax.set_axisbelow(True)
    suffix='-mobile' if compact else ''
    fig.savefig(base/f'cashflow{suffix}.svg',bbox_inches='tight')
    svg_path=base/f'cashflow{suffix}.svg'
    svg_path.write_text('\n'.join(line.rstrip() for line in svg_path.read_text().splitlines())+'\n')
    fig.savefig(base/f'cashflow{suffix}.png',dpi=150,bbox_inches='tight')
    plt.close(fig)
print(json.dumps(metrics, indent=2))
