"""Local fictional learning reference. No website/database/cloud connection."""
import csv,json,sqlite3,hashlib,math,re,statistics,collections
from pathlib import Path

def rows(source,name):
 with (Path(source)/name).open(encoding='utf-8',newline='') as f:return list(csv.DictReader(f))

def fit_service(source):
 import numpy as np
 data=[r for r in rows(source,'telemetry.csv') if r['period']=='train'];x=np.array([[1,float(r['distance_km']),float(r['load_ratio'])] for r in data]);y=np.array([float(r['actual_hours']) for r in data]);coef=np.linalg.lstsq(x,y,rcond=None)[0]
 return {'schema_version':1,'model_id':'haven-duration-v1','features':['distance_km','load_ratio'],'coefficients':coef.tolist(),'target_unit':'hours','training_rows':len(data),'bounds':{'distance_km':[0,250],'load_ratio':[0,1.2]}}

def predict_payload(payload,model):
 if not isinstance(payload,dict) or set(payload)!=set(model['features']):raise ValueError('Exactly distance_km and load_ratio are required')
 values=[]
 for key in model['features']:
  value=payload[key]
  if isinstance(value,bool) or not isinstance(value,(int,float)) or not math.isfinite(value):raise ValueError('Finite numeric input required')
  lo,hi=model['bounds'][key]
  if not lo<=value<=hi:raise ValueError('Outside supported teaching range')
  values.append(value)
 answer=model['coefficients'][0]+sum(c*v for c,v in zip(model['coefficients'][1:],values))
 return {'model_id':model['model_id'],'prediction_hours':answer,'unit':'hours','automated_decision':False}

def mlops_tracking(source,out):
 model=fit_service(source);raw=json.dumps(model,sort_keys=True).encode();traces=[]
 for period in ['validation','test']:
  d=[r for r in rows(source,'telemetry.csv') if r['period']==period]
  errors=[abs(float(r['actual_hours'])-predict_payload({'distance_km':float(r['distance_km']),'load_ratio':float(r['load_ratio'])},model)['prediction_hours']) for r in d]
  traces.append({'partition':period,'n':len(d),'mae_hours':sum(errors)/len(errors),'training_mean_baseline_mae_hours':sum(abs(float(r['actual_hours'])-statistics.mean(float(t['actual_hours']) for t in rows(source,'telemetry.csv') if t['period']=='train')) for r in d)/len(d)})
 return {'experiment_id':'haven-duration-v1-reference','model_sha256':hashlib.sha256(raw).hexdigest(),'source_sha256':hashlib.sha256((Path(source)/'telemetry.csv').read_bytes()).hexdigest(),'metrics':traces,'training_rows':40,'selection_note':'Fixed model; test is final reporting only, not used for selection.'}

if __name__=='__main__':
    import argparse,platform
    parser=argparse.ArgumentParser();parser.add_argument('--source',default='.');parser.add_argument('--output',required=True);args=parser.parse_args()
    source=Path(args.source).resolve();out=Path(args.output).resolve()
    if source==out:raise ValueError('Source and output must differ')
    out.mkdir(parents=False,exist_ok=False)
    # Partial folders after an error are NOT successful; result.json is written last.
    result=mlops_tracking(source,out)
    with (out/'result.json').open('x',encoding='utf-8') as handle:json.dump(result,handle,indent=2,allow_nan=False);handle.write(chr(10))
    print(json.dumps(result,indent=2,allow_nan=False))
