"""Reference solution for Partner pilot analysis project: separate attributed sales from incremental sales.
Original synthetic teaching data only. Python 3.10+, standard library.
Run python solve.py [path-to-project-inputs]. No network or credentials.
"""
from pathlib import Path
from datetime import datetime, date
import csv, json, math, statistics, sys

def read(root, name):
    with (Path(root)/name).open(newline='',encoding='utf-8') as f:
        reader=csv.DictReader(f)
        if not reader.fieldnames or len(reader.fieldnames)!=len(set(reader.fieldnames)):
            raise ValueError(f'{name}: missing or duplicate headers')
        rows=list(reader)
        if any(None in row or any(v is None for v in row.values()) for row in rows):
            raise ValueError(f'{name}: inconsistent row length')
        return rows

def rules(root): return json.loads((Path(root)/'rules.json').read_text())

def number(value):
    x=float(value)
    if not math.isfinite(x): raise ValueError('Non-finite input')
    return x

def nonnegative(value):
    x=number(value)
    if x<0:raise ValueError('Negative quantity or amount')
    return x

def flag(value):
    if value not in ('true','false'):raise ValueError('Boolean must be true or false')
    return value=='true'

def timestamp(value):
    d=datetime.fromisoformat(value.replace('Z','+00:00'))
    if d.tzinfo is None:raise ValueError('Timestamp requires an explicit time zone')
    return d

def unique(rows, keys, ignore=()):
    by={};duplicates=0
    for row in rows:
        key=tuple(row[k] for k in keys)
        if any(not k for k in key):raise ValueError('Missing record identity')
        if key in by:
            prior={k:v for k,v in by[key].items() if k not in ignore}
            new={k:v for k,v in row.items() if k not in ignore}
            if prior!=new:raise ValueError(f'Conflicting records at {key}')
            duplicates+=1
        else:by[key]=row
    return list(by.values()),duplicates

def rounded(value):
    if isinstance(value,float): return round(value,8)
    if isinstance(value,dict):return {k:rounded(v) for k,v in value.items()}
    if isinstance(value,list):return [rounded(v) for v in value]
    return value

def month_index(s):
    d=datetime.strptime(s,'%Y-%m')
    if d.strftime('%Y-%m')!=s:raise ValueError('Use YYYY-MM')
    return d.year*12+d.month

def partner_pilot(root):
    r=rules(root);rows,_=unique(read(root,'accounts.csv'),['account_id']);referrals,retries=unique(read(root,'referrals.csv'),['referral_id'])
    known={x['account_id'] for x in rows}
    if any(x['account_id'] not in known for x in referrals):raise ValueError('Unknown referred account')
    arms={k:[x for x in rows if x['assigned_arm']==k] for k in ('invited','holdout')}
    if sum(map(len,arms.values()))!=len(rows) or any(not v for v in arms.values()):raise ValueError('Invalid experiment arms')
    n1,n0=len(arms['invited']),len(arms['holdout']);k1=sum(flag(x['converted']) for x in arms['invited']);k0=sum(flag(x['converted']) for x in arms['holdout'])
    won=[x for x in rows if flag(x['converted'])]
    margins={number(x['first_year_revenue'])-number(x['variable_delivery_cost']) for x in won}
    if len(margins)!=1:raise ValueError('This equal-margin teaching model requires one unit contribution')
    unit=margins.pop();difference=k1/n1-k0/n0;incremental=difference*n1
    tagged=[x for x in arms['invited'] if flag(x['converted']) and flag(x['partner_tagged'])]
    commission=sum(number(x['first_year_revenue'])*r['commission_rate'] for x in tagged)
    cost=commission+k1*r['incremental_onboarding_per_invited_win']+r['setup_cost']
    N=n1+n0;K=k1+k0;den=math.comb(N,n1)
    p=lambda x:math.comb(K,x)*math.comb(N-K,n1-x)/den
    observed=p(k1);pvalue=sum(p(x) for x in range(max(0,n1-(N-K)),min(n1,K)+1) if p(x)<=observed+1e-12)
    return {'invited_accounts':n1,'holdout_accounts':n0,'invited_wins':k1,'holdout_wins':k0,'partner_tagged_wins':len(tagged),'unique_referral_rows':len(referrals),'retry_referrals_removed':retries,'conversion_difference':difference,'incremental_wins_point_estimate':incremental,'unit_contribution':unit,'incremental_program_cost':cost,'incremental_contribution_point_estimate':incremental*unit-cost,'two_sided_exact_p':pvalue,'break_even_extra_wins':cost/unit}

def main():
    directory=Path(sys.argv[1]) if len(sys.argv)>1 else Path(__file__).resolve().parent
    try:
        result=partner_pilot(directory)
        print(json.dumps(rounded(result),indent=2,allow_nan=False))
    except (OSError,ValueError,KeyError,TypeError,ZeroDivisionError) as exc:
        print(f"Input could not be analyzed: {exc}",file=sys.stderr);return 2
    return 0
if __name__=='__main__':raise SystemExit(main())
