#!/usr/bin/env python3
"""Standalone dashboard generator — reads handoff JSON, writes bot_dashboard.html"""
import sys, os, json, time, csv, re, logging, pytz
sys.path.insert(0, '/home/opc/trading')
os.chdir('/home/opc/trading')

DISPLAY_TZ = pytz.timezone('America/Chicago')
MARKET_TZ  = pytz.timezone('America/New_York')
from datetime import datetime

logging.basicConfig(level=logging.INFO, format='%(message)s')
logger = logging.getLogger('dash')
debug_logger = logger

src = open('StockTrading.py').read()

def get_const(name, default):
    m = re.search(rf'^{name}\s*=\s*([0-9_\.]+)', src, re.MULTILINE)
    return float(m.group(1).replace('_','')) if m else default

DAILY_TRAIN_TREES    = int(get_const('DAILY_TRAIN_TREES',   50))
WEEKLY_TRAIN_TREES   = int(get_const('WEEKLY_TRAIN_TREES', 100))
DISTILL_THRESHOLD    = int(get_const('DISTILL_THRESHOLD',  2500))
DISTILL_BASE_TREES   = int(get_const('DISTILL_BASE_TREES',  300))
IC_MIN_VOTE_THRESHOLD= get_const('IC_MIN_VOTE_THRESHOLD',  0.015)
STARTING_CAPITAL     = get_const('STARTING_CAPITAL',       1000.0)
NUM_AGENTS           = int(get_const('NUM_AGENTS',          32))
N_FEATURES           = int(get_const('N_FEATURES',          56))
MIN_ROWS_TO_TRADE    = int(get_const('MIN_ROWS_TO_TRADE',   60))
MIN_ROWS_TO_TRAIN    = int(get_const('MIN_ROWS_TO_TRAIN',   50))
MAX_WORKERS_CAP      = int(get_const('MAX_WORKERS_CAP',     8))

# Extract just the list value from FEATURE_COLS = [...]
FEATURE_COLS = []
m = re.search(r'FEATURE_COLS\s*=\s*(\[)', src)
if m:
    list_start = m.start(1)          # start of the '[' character
    depth = 0
    for j, ch in enumerate(src[list_start:]):
        if ch == '[': depth += 1
        elif ch == ']':
            depth -= 1
            if depth == 0:
                FEATURE_COLS = eval(src[list_start:list_start+j+1])
                break

def is_market_open():
    now = datetime.now(MARKET_TZ)
    if now.weekday() >= 5: return False
    return (9,30) <= (now.hour, now.minute) <= (16,0)

def seconds_until_open():
    from datetime import timedelta
    now = datetime.now(MARKET_TZ)
    nxt = now.replace(hour=9,minute=30,second=0,microsecond=0)
    if now >= nxt or now.weekday() >= 5:
        days = 1
        while True:
            nxt += timedelta(days=days)
            if nxt.weekday() < 5: break
            days = 1
    return max(0,(nxt-now).total_seconds())

class FakeModel:
    def __init__(self, a):
        self.feat_importance = dict(a.get('top_features', {}))
        self.ic_history  = a.get('ic_history', [])
        self._train_count= a.get('train_count', 0)
        self.val_acc     = a.get('val_acc',   0.0)
        self.train_acc   = a.get('train_acc', 0.0)
        self.trained     = a.get('trained', True)
    def rolling_ic(self, n=20):
        h = self.ic_history
        if len(h) < 3: return 0.0
        return float(sum(h[-n:])/len(h[-n:]))
    @property
    def tree_count(self): return self._train_count * 50

class FakeAgent:
    def __init__(self, a):
        self.name            = a['name']
        self.is_clone        = a.get('type') == 'clone'
        self.model           = FakeModel(a)
        self.total_pnl       = a.get('total_pnl',  0.0)
        self.trades          = a.get('trades',        0)
        self.wins            = a.get('wins',           0)
        self.positions       = {}
        self.capital         = a.get('capital',    1000.0)
        self.stop_loss_pct   = a.get('stop_loss_pct',  0.07)
        self.take_profit_pct = a.get('take_profit_pct',0.15)
        self.min_conf        = a.get('min_conf',        0.7)
        self.max_pos_pct     = a.get('max_pos_pct',     0.2)
        self.history         = []
        self.gross_profit    = a.get('gross_profit', 0.0)
        self.gross_loss      = a.get('gross_loss',   0.0)
    def total_equity(self, prices): return self.capital + self.total_pnl

class DummyDM:
    features={}; prices={}
class DummyMeta: pass

handoff = json.load(open('logs/claude_handoff.json'))
agents  = [FakeAgent(a) for a in handoff.get('agents', [])]
print(f"Loaded {len(agents)} agents, FEATURE_COLS={len(FEATURE_COLS)}")

fi = src.index('def _write_html_dashboard(')
fe = src.index('\ndef _write_claude_handoff(', fi)
func_code = src[fi:fe]

g = {
    'json':json,'os':os,'time':time,'csv':csv,'datetime':datetime,
    'logging':logging,'DISPLAY_TZ':DISPLAY_TZ,'MARKET_TZ':MARKET_TZ,
    'DAILY_TRAIN_TREES':DAILY_TRAIN_TREES,'WEEKLY_TRAIN_TREES':WEEKLY_TRAIN_TREES,
    'DISTILL_THRESHOLD':DISTILL_THRESHOLD,'DISTILL_BASE_TREES':DISTILL_BASE_TREES,
    'IC_MIN_VOTE_THRESHOLD':IC_MIN_VOTE_THRESHOLD,'STARTING_CAPITAL':STARTING_CAPITAL,
    'NUM_AGENTS':NUM_AGENTS,'N_FEATURES':N_FEATURES,'MIN_ROWS_TO_TRADE':MIN_ROWS_TO_TRADE,
    'MIN_ROWS_TO_TRAIN':MIN_ROWS_TO_TRAIN,'MAX_WORKERS_CAP':MAX_WORKERS_CAP,
    'FEATURE_COLS':FEATURE_COLS,'DataManager':type('DM',(),{}),'MetaLearner':type('ML',(),{}),
    'logger':logger,'debug_logger':debug_logger,
    'is_market_open':is_market_open,'seconds_until_open':seconds_until_open,
}
exec(compile(func_code, 'dashboard_func', 'exec'), g)
g['_write_html_dashboard'](agents, DummyDM(), DummyMeta())

sz = os.path.getsize('logs/bot_dashboard.html')
print(f'Dashboard written: {sz:,} bytes ({sz//1024}KB)')
