#!/usr/bin/env python3
from decimal import Decimal, InvalidOperation
import argparse, json
from pathlib import Path

D=Decimal
OP={'data_centre':D('0.055'),'network':D('0.059'),'device':D('0.080')}
EM={'data_centre':D('0.012'),'network':D('0.013'),'device':D('0.081')}
EM_GRID=D('494')

def dec(v,n,u=None):
    if v is None or isinstance(v,bool): raise ValueError(f'{n}: explicit finite number required')
    try: x=D(str(v))
    except (InvalidOperation,ValueError) as e: raise ValueError(f'{n}: invalid') from e
    if not x.is_finite() or x<0 or (u is not None and x>D(u)): raise ValueError(f'{n}: range')
    return x

def estimate(byte_count, first_share='1', return_share='0', reload_share='0', hosting_factor='0',
             dc_grid='494', network_grid='494', device_grid='494'):
    b=dec(byte_count,'bytes')
    if b != b.to_integral_value(): raise ValueError('bytes must be integer')
    first=dec(first_share,'first_share','1'); ret=dec(return_share,'return_share','1')
    reload=dec(reload_share,'reload_share','1'); host=dec(hosting_factor,'hosting_factor','1')
    if first+ret != 1: raise ValueError('first + return must equal 1')
    grids={'data_centre':dec(dc_grid,'dc_grid'),'network':dec(network_grid,'network_grid'),'device':dec(device_grid,'device_grid')}
    factor=first+ret*reload; gb=b/D('1000000000')
    total=D(0)
    for seg in OP:
        op=gb*OP[seg]*grids[seg]
        em=gb*EM[seg]*EM_GRID
        total += op*(1-host if seg=='data_centre' else 1)*factor + em*factor
    return total

def main():
    cases=[
      ('one_mb_cold', dict(byte_count=1000000),'0.148200'),
      ('one_mb_hosting_adjustment',dict(byte_count=1000000,hosting_factor='1'),'0.121030'),
      ('one_mb_mixed_visits',dict(byte_count=1000000,first_share='.5',return_share='.5',reload_share='.2'),'0.088920'),
      ('one_mb_hosting_and_mixed_visits',dict(byte_count=1000000,first_share='.5',return_share='.5',reload_share='.2',hosting_factor='1'),'0.072618'),
      ('two_mb_cold',dict(byte_count=2000000),'0.296400'),
      ('one_mib_cold',dict(byte_count=1048576),'0.155399'),
    ]
    out=[]
    for name,args,expected in cases:
        value=format(estimate(**args),'.6f')
        assert value==expected,(name,value,expected)
        out.append({'id':name,'result':value})
    print(json.dumps({'status':'PASS','cases':out},indent=2))

if __name__=='__main__':
    main()
