"""Independent direct-slot arithmetic. No execution on import; no author-runner imports."""
from pathlib import Path
from datetime import datetime,timedelta,timezone
from zoneinfo import ZoneInfo
from decimal import Decimal
from collections import defaultdict
from fractions import Fraction
import csv,hashlib,random

BASE=Path(__file__).resolve().parents[1]/'storage-test'

def raw_year(year):
    rows=[]
    with (BASE/'raw'/f'eco2mix-{year}-selected.csv').open(encoding='utf-8-sig',newline='') as f:
        for r in csv.DictReader(f,delimiter=';'):
            vs=[r[k].strip() for k in ('consommation','eolien','solaire')]
            if not any(vs): continue
            assert all(vs)
            dec=[Decimal(v) for v in vs]
            assert all(x.is_finite() and x==x.to_integral() for x in dec)
            rows.append((r['date'],r['heure'],*[int(x) for x in dec]))
    rows.sort()
    assert len({r[:2] for r in rows})==len(rows)
    return rows

def transition_dates(year):
    paris=ZoneInfo('Europe/Paris');d=datetime(year,1,1);dates=[]
    while d.year==year:
        nd=d+timedelta(days=1)
        a=d.replace(tzinfo=paris).astimezone(timezone.utc)
        b=nd.replace(tzinfo=paris).astimezone(timezone.utc)
        if (b-a).total_seconds()!=86400: dates.append(d.date().isoformat())
        d=nd
    assert len(dates)==2
    return set(dates)

def build(year,scenario):
    rows=raw_year(year)
    if scenario['date_policy']=='exclude_both_Paris_DST_transition_dates':
        exclude=transition_dates(year); rows=[r for r in rows if r[0] not in exclude]
    if scenario['negative_policy']=='max_zero_W_and_S_before_normalization':
        rows=[(d,t,L,max(W,0),max(S,0)) for d,t,L,W,S in rows]
    sums=[sum(r[i] for r in rows) for i in (2,3,4)]
    totalL,totalW,totalS=sums
    assert min(sums)>0
    # Deliberately use reduced Fraction coefficients independent of author's integer scale.
    wind=Fraction(scenario['wind_share_percent'],100)
    # common denominator of exact coefficient fractions
    alpha=Fraction(totalL,1)*wind/totalW
    beta=Fraction(totalL,1)*(1-wind)/totalS
    import math
    den=math.lcm(alpha.denominator,beta.denominator)
    aw=int(alpha*den);bs=int(beta*den)
    slots=[(d,t,den*L-aw*W-bs*S) for d,t,L,W,S in rows]
    assert sum(N for d,t,N in slots)==0
    days=defaultdict(list)
    for d,t,N in slots: days[d].append(N)
    assert all(len(v)==48 for v in days.values())
    dates=sorted(days)
    return {'year':year,'scenario':scenario['id'],'denominator':den,'rows':rows,'days':dict(days),'dates':dates,'annual_sample_sums':sums,'alpha':str(alpha),'beta':str(beta),'N_sum':0}

def direct_excursion(data,dates=None):
    acc=0;low=0;high=0
    for day in (data['dates'] if dates is None else dates):
        for N in data['days'][day]:
            acc+=N
            low=min(low,acc);high=max(high,acc)
    assert acc==0
    return {'range_numerator':high-low,'denominator':2*data['denominator'],'capacity_exact':Fraction(high-low,2*data['denominator']),'prefix_min':low,'prefix_max':high,'final_prefix':acc}

def permutation_dates(data,replicate):
    text=f"incoprea-storage-v2|year={data['year']}|scenario={data['scenario']}|replicate={replicate}"
    seed=int.from_bytes(hashlib.sha256(text.encode()).digest()[:8],'big')
    rng=random.Random(seed);months=defaultdict(list)
    for d in data['dates']:months[d[:7]].append(d)
    order=[]
    for month in sorted(months):
        ds=months[month].copy();rng.shuffle(ds);order.extend(ds)
    assert sorted(order)==data['dates']
    return seed,order

def linear_quantile(values,p):
    x=sorted(values);v=(len(x)-1)*p;i=int(v);f=v-i
    return x[i]+f*(x[min(i+1,len(x)-1)]-x[i])
