#!/usr/bin/env python3
"""
training_status_printer.py — Prints training data collection status to the POS printer.

Tracks:
- Gemini API usage and estimated cost
- Training audit log growth
- Bot activity
- Model performance snapshot

Runs on a 4-hour interval via cron or direct invocation.

Usage:
    python3 training_status_printer.py          # print now
    python3 training_status_printer.py --dry-run # show what would print
"""

import json
import os
import socket
import subprocess
import sys
import time
from datetime import datetime, timedelta
from pathlib import Path

# --- Config ---

PRINTER_IP = "192.168.0.137"
PRINTER_PORT = 9100
COLS = 57

# Gemini Flash Lite pricing (per 1M tokens, as of 2026-03)
# https://ai.google.dev/pricing
GEMINI_INPUT_COST_PER_M = 0.075   # $0.075 per 1M input tokens
GEMINI_OUTPUT_COST_PER_M = 0.30   # $0.30 per 1M output tokens
# Approximate: our prompt is ~300 tokens input, ~200 tokens output per call
EST_INPUT_TOKENS_PER_CALL = 300
EST_OUTPUT_TOKENS_PER_CALL = 200

# Gemini usage tracking file
GEMINI_USAGE_FILE = "/var/log/mc_gemini_usage.json"

# Cost threshold for printing ($)
COST_PRINT_THRESHOLD = 0.50
LAST_PRINT_COST_FILE = "/var/log/mc_training_last_print_cost.json"

# Remote paths (on CT 644 via pve112)
DEV_AUDIT_LOG = "/var/log/mc_training_audit_dev.jsonl"
PROD_AUDIT_LOG = "/var/log/mc_training_audit.jsonl"
BOT_LOG = "/var/log/prayer_bots.log"
DEV_BUG_LOG = "/var/log/mc_aigod_dev_bug.log"


def remote_cmd(cmd, timeout=10):
    """Run a command on CT 644 via pve112."""
    try:
        full_cmd = f'ssh pve112 "pct exec 644 -- {cmd}"'
        result = subprocess.run(
            full_cmd, shell=True,
            capture_output=True, text=True, timeout=timeout
        )
        return result.stdout.strip()
    except Exception as e:
        return f"ERROR: {e}"


def get_audit_stats():
    """Get training audit log stats from both servers."""
    dev_lines = remote_cmd(f"wc -l {DEV_AUDIT_LOG} 2>/dev/null | cut -d' ' -f1")
    prod_lines = remote_cmd(f"wc -l {PROD_AUDIT_LOG} 2>/dev/null | cut -d' ' -f1")
    bug_lines = remote_cmd(f"wc -l {DEV_BUG_LOG} 2>/dev/null | cut -d' ' -f1")

    try:
        dev_count = int(dev_lines) if dev_lines.isdigit() else 0
    except:
        dev_count = 0
    try:
        prod_count = int(prod_lines) if prod_lines.isdigit() else 0
    except:
        prod_count = 0
    try:
        bug_count = int(bug_lines) if bug_lines.isdigit() else 0
    except:
        bug_count = 0

    return dev_count, prod_count, bug_count


def get_bot_stats():
    """Get bot activity stats."""
    bot_procs = remote_cmd("ps aux | grep prayer_bots | grep -v grep | wc -l")
    bot_last = remote_cmd(f"tail -1 {BOT_LOG} 2>/dev/null")
    bot_sends = remote_cmd(f"grep -c 'SEND' {BOT_LOG} 2>/dev/null")

    try:
        num_bots = int(bot_procs)
    except:
        num_bots = 0
    try:
        num_sends = int(bot_sends)
    except:
        num_sends = 0

    return num_bots, num_sends, bot_last[:60] if bot_last else "N/A"


def get_gemini_usage():
    """Track Gemini API calls from bot log."""
    gemini_calls = remote_cmd(f"grep -c 'Gemini.*Generated' {BOT_LOG} 2>/dev/null")
    gemini_errors = remote_cmd(f"grep -c 'Gemini.*Error' {BOT_LOG} 2>/dev/null")

    try:
        calls = int(gemini_calls)
    except:
        calls = 0
    try:
        errors = int(gemini_errors)
    except:
        errors = 0

    total_input_tokens = calls * EST_INPUT_TOKENS_PER_CALL
    total_output_tokens = calls * EST_OUTPUT_TOKENS_PER_CALL
    input_cost = (total_input_tokens / 1_000_000) * GEMINI_INPUT_COST_PER_M
    output_cost = (total_output_tokens / 1_000_000) * GEMINI_OUTPUT_COST_PER_M
    total_cost = input_cost + output_cost

    return {
        "calls": calls,
        "errors": errors,
        "est_input_tokens": total_input_tokens,
        "est_output_tokens": total_output_tokens,
        "est_cost_usd": total_cost,
    }


def get_haiku_usage():
    """Track Claude Haiku API spend from persistent cost file."""
    raw = remote_cmd("cat /var/log/mc_anthropic_cost.json 2>/dev/null")
    cost = 0.0
    budget = 5.0
    if raw and raw.startswith("{"):
        try:
            import json as _json
            data = _json.loads(raw)
            cost = data.get("total_cost", 0.0)
        except:
            pass
    return {"cost": cost, "budget": budget}


def get_dataset_size():
    """Get current seed dataset size."""
    try:
        path = Path(__file__).resolve().parent.parent / "data" / "processed" / "seed_dataset.jsonl"
        with open(path) as f:
            return sum(1 for line in f if line.strip())
    except:
        return 0


def get_service_status():
    """Check if AI God services are running."""
    statuses = {}
    for svc in ["mc-aigod-paper", "mc-aigod-dev", "mc-aigod"]:
        status = remote_cmd(f"systemctl is-active {svc}.service 2>/dev/null")
        statuses[svc] = status
    return statuses


def build_receipt():
    """Build the POS receipt."""
    from escpos.printer import Dummy

    now = datetime.now()
    p = Dummy(profile="default")

    # Header
    p.set(font='b', align='center', bold=True, height=2)
    p.text("MC AI TRAINING\n")
    p.set(font='b', align='center', bold=True, height=1)
    p.text("STATUS REPORT\n")
    p.set(font='b', align='center', bold=False)
    p.text(now.strftime("%Y-%m-%d %H:%M") + "\n")
    p.text("=" * COLS + "\n")

    # Dataset
    dataset_size = get_dataset_size()
    p.set(font='b', align='left', bold=True)
    p.text("DATASET\n")
    p.set(font='b', align='left', bold=False)
    p.text(f"  Seed examples:     {dataset_size}\n")

    dev_audit, prod_audit, bug_count = get_audit_stats()
    p.text(f"  Dev audit log:     {dev_audit}\n")
    p.text(f"  Prod audit log:    {prod_audit}\n")
    p.text(f"  Dev bug reports:   {bug_count}\n")
    p.text(f"  Total unprocessed: {dev_audit + prod_audit}\n")
    p.text("-" * COLS + "\n")

    # Bot activity
    num_bots, num_sends, last_msg = get_bot_stats()
    p.set(font='b', align='left', bold=True)
    p.text("BOT ACTIVITY\n")
    p.set(font='b', align='left', bold=False)
    p.text(f"  Active bots:       {num_bots}\n")
    p.text(f"  Total messages:    {num_sends}\n")
    p.text(f"  Last: {last_msg}\n")
    p.text("-" * COLS + "\n")

    # Haiku API (main cost)
    haiku = get_haiku_usage()
    p.set(font='b', align='left', bold=True)
    p.text("CLAUDE HAIKU API (dev God)\n")
    p.set(font='b', align='left', bold=False)
    p.set(font='b', align='left', bold=True)
    p.text(f"  Spent:             ${haiku['cost']:.4f}\n")
    p.set(font='b', align='left', bold=False)
    p.text(f"  Budget:            ${haiku['budget']:.2f}\n")
    p.text(f"  Remaining:         ${haiku['budget'] - haiku['cost']:.4f}\n")
    p.text("-" * COLS + "\n")

    # Gemini API (bot prompts)
    gemini = get_gemini_usage()
    p.set(font='b', align='left', bold=True)
    p.text("GEMINI API (bot prompts)\n")
    p.set(font='b', align='left', bold=False)
    p.text(f"  Calls:             {gemini['calls']}\n")
    p.text(f"  Errors:            {gemini['errors']}\n")
    p.set(font='b', align='left', bold=True)
    p.text(f"  Est cost:          ${gemini['est_cost_usd']:.4f}\n")
    p.set(font='b', align='left', bold=False)
    p.text("-" * COLS + "\n")

    # Services
    statuses = get_service_status()
    p.set(font='b', align='left', bold=True)
    p.text("SERVICES\n")
    p.set(font='b', align='left', bold=False)
    for svc, status in statuses.items():
        indicator = "OK" if status == "active" else "DOWN"
        p.text(f"  {svc:22} [{indicator}]\n")
    p.text("-" * COLS + "\n")

    # Footer
    p.set(font='b', align='center', bold=False)
    p.text("Next print in 4 hours\n")
    p.text("=" * COLS + "\n")
    p.cut()

    return p.output


def send_to_printer(raw_bytes):
    """Send raw ESC/POS bytes to the TM-m30."""
    with socket.create_connection((PRINTER_IP, PRINTER_PORT), timeout=10) as sock:
        sock.sendall(raw_bytes)


def get_last_print_cost():
    """Get the cumulative cost at which we last printed."""
    try:
        with open(LAST_PRINT_COST_FILE) as f:
            return json.load(f).get("cost", 0.0)
    except:
        return 0.0


def save_last_print_cost(cost):
    """Save the cumulative cost at which we printed."""
    with open(LAST_PRINT_COST_FILE, "w") as f:
        json.dump({"cost": cost, "timestamp": datetime.now().isoformat()}, f)


def should_print(current_cost):
    """Check if we've crossed the next $COST_PRINT_THRESHOLD boundary."""
    last_cost = get_last_print_cost()
    return current_cost - last_cost >= COST_PRINT_THRESHOLD


def main():
    dry_run = "--dry-run" in sys.argv
    force = "--force" in sys.argv
    check_only = "--check" in sys.argv

    gemini = get_gemini_usage()
    current_cost = gemini["est_cost_usd"]

    if check_only:
        last = get_last_print_cost()
        next_at = last + COST_PRINT_THRESHOLD
        print(f"Current cost:    ${current_cost:.4f}")
        print(f"Last printed at: ${last:.4f}")
        print(f"Next print at:   ${next_at:.4f}")
        print(f"Will print:      {'YES' if should_print(current_cost) else 'NO'}")
        return

    if dry_run:
        print("=== DRY RUN — would print: ===\n")

        dataset_size = get_dataset_size()
        dev_audit, prod_audit, bug_count = get_audit_stats()
        num_bots, num_sends, last_msg = get_bot_stats()
        haiku = get_haiku_usage()
        statuses = get_service_status()

        print(f"Dataset:        {dataset_size} seed examples")
        print(f"Dev audit:      {dev_audit} entries")
        print(f"Prod audit:     {prod_audit} entries")
        print(f"Bug reports:    {bug_count}")
        print(f"Bots:           {num_bots} active, {num_sends} messages sent")
        print(f"Haiku:          ${haiku['cost']:.4f} / ${haiku['budget']:.2f} ({haiku['budget'] - haiku['cost']:.4f} remaining)")
        print(f"Gemini:         {gemini['calls']} calls, {gemini['errors']} errors, ${gemini['est_cost_usd']:.4f}")
        print(f"Services:       {statuses}")
        return

    if not force and not should_print(current_cost):
        print(f"[{datetime.now().isoformat()}] Cost ${current_cost:.4f} — threshold not reached (next at ${get_last_print_cost() + COST_PRINT_THRESHOLD:.4f})")
        return

    receipt = build_receipt()
    try:
        send_to_printer(receipt)
        save_last_print_cost(current_cost)
        print(f"[{datetime.now().isoformat()}] Receipt printed at ${current_cost:.4f}")
    except Exception as e:
        print(f"[{datetime.now().isoformat()}] Print failed: {e}")


if __name__ == "__main__":
    main()
