#!/usr/bin/env python3
"""
NEOG Gateway - Unified API for NEOG Command macOS App
Centralizes CC ROI Tracking with embedded deployment support.

Starts automatically with NEOG Command app and provides:
- CC ROI endpoints (runs, budget, gates)
- Gateway status and health
- Real-time WebSocket updates

Port: 9001 (default, configurable via NEOG_GATEWAY_PORT)
"""

from flask import Flask, request, jsonify
from flask_cors import CORS
from flask_socketio import SocketIO, emit
import sqlite3
import json
import uuid
import os
import time
import subprocess
from datetime import datetime
from functools import wraps

# ========== CONFIGURATION ==========

GATEWAY_VERSION = "1.0.0"
GATEWAY_START_TIME = time.time()
PORT = int(os.environ.get('NEOG_GATEWAY_PORT', 9001))
DB_PATH = os.path.expanduser('~/logs.db')

# Token cost rates (USD per 1M tokens)
TOKEN_RATES = {
    'claude-opus-4-5-20251101': {'input': 15.00, 'output': 75.00, 'cache_read': 1.50},
    'claude-sonnet-4-5-20250929': {'input': 3.00, 'output': 15.00, 'cache_read': 0.30},
    'default': {'input': 3.00, 'output': 15.00, 'cache_read': 0.30}
}

# ========== FLASK APP ==========

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading')

# ========== DATABASE ==========

def get_db():
    """Get database connection with row factory"""
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn

def init_db():
    """Initialize database with CC tracking tables"""
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()

    # cc_runs: Track automation execution sessions
    c.execute('''CREATE TABLE IF NOT EXISTS cc_runs (
        run_id TEXT PRIMARY KEY,
        session_id TEXT,
        prompt TEXT NOT NULL,
        source TEXT DEFAULT 'manual',
        device_ip TEXT,
        status TEXT DEFAULT 'pending',
        model TEXT DEFAULT 'claude-opus-4-5-20251101',

        input_tokens INTEGER DEFAULT 0,
        output_tokens INTEGER DEFAULT 0,
        cache_read_tokens INTEGER DEFAULT 0,

        cost_usd REAL DEFAULT 0,
        value_generated_usd REAL DEFAULT 0,
        value_is_estimated BOOLEAN DEFAULT 1,

        gate_guardrails_ok BOOLEAN DEFAULT 1,
        gate_budget_ok BOOLEAN DEFAULT 1,
        gate_heartbeat_ok BOOLEAN DEFAULT 1,
        halt_reason TEXT,

        started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        completed_at DATETIME,
        duration_ms INTEGER,

        tools_used TEXT,
        files_modified TEXT,
        error_message TEXT,
        tags TEXT
    )''')

    # cc_events: Granular events within each run
    c.execute('''CREATE TABLE IF NOT EXISTS cc_events (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        run_id TEXT NOT NULL,
        event_type TEXT NOT NULL,
        event_subtype TEXT,
        input_tokens INTEGER DEFAULT 0,
        output_tokens INTEGER DEFAULT 0,
        cost_usd REAL DEFAULT 0,
        input_data TEXT,
        output_data TEXT,
        duration_ms INTEGER,
        success BOOLEAN DEFAULT 1,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (run_id) REFERENCES cc_runs(run_id)
    )''')

    # budget_config: 3-Gate budget limits
    c.execute('''CREATE TABLE IF NOT EXISTS budget_config (
        id INTEGER PRIMARY KEY,
        name TEXT UNIQUE NOT NULL,
        daily_limit_usd REAL DEFAULT 10.00,
        monthly_limit_usd REAL DEFAULT 200.00,
        max_tokens_per_run INTEGER DEFAULT 100000,
        max_cost_per_run_usd REAL DEFAULT 2.00,
        alert_threshold_pct INTEGER DEFAULT 80,
        is_active BOOLEAN DEFAULT 1,
        updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )''')

    # guardrails: Safety rules
    c.execute('''CREATE TABLE IF NOT EXISTS guardrails (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        rule_type TEXT NOT NULL,
        pattern TEXT NOT NULL,
        action TEXT DEFAULT 'block',
        is_active BOOLEAN DEFAULT 1,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )''')

    # Indexes
    c.execute('CREATE INDEX IF NOT EXISTS idx_cc_runs_status ON cc_runs(status)')
    c.execute('CREATE INDEX IF NOT EXISTS idx_cc_runs_started ON cc_runs(started_at)')
    c.execute('CREATE INDEX IF NOT EXISTS idx_cc_events_run ON cc_events(run_id)')

    # Seed default budget config
    c.execute('''INSERT OR IGNORE INTO budget_config (id, name, daily_limit_usd, monthly_limit_usd)
                 VALUES (1, 'default', 10.00, 200.00)''')

    # Seed default guardrails
    guardrails_seed = [
        ('block_rm_rf', 'blocked_command', 'rm -rf /', 'block'),
        ('block_rm_rf_home', 'blocked_command', 'rm -rf ~', 'block'),
        ('block_sudo', 'blocked_command', 'sudo', 'warn'),
        ('block_force_push', 'blocked_command', 'git push --force', 'warn'),
        ('block_secrets', 'blocked_path', '**/*secret*', 'block'),
        ('block_env', 'blocked_path', '.env', 'warn'),
        ('block_ssh', 'blocked_path', '.ssh', 'block'),
    ]
    for name, rule_type, pattern, action in guardrails_seed:
        c.execute('''INSERT OR IGNORE INTO guardrails (name, rule_type, pattern, action)
                     VALUES (?, ?, ?, ?)''', (name, rule_type, pattern, action))

    conn.commit()
    conn.close()
    print(f"[GATEWAY] Database initialized at {DB_PATH}")

# ========== HELPERS ==========

def calculate_cost(input_tokens, output_tokens, cache_read_tokens=0, model='default'):
    """Calculate cost in USD based on token usage"""
    rates = TOKEN_RATES.get(model, TOKEN_RATES['default'])
    cost = (input_tokens / 1_000_000 * rates['input'] +
            output_tokens / 1_000_000 * rates['output'] +
            cache_read_tokens / 1_000_000 * rates['cache_read'])
    return round(cost, 6)

def estimate_run_value(prompt, source, cost_usd, tokens):
    """Smart value estimation based on run characteristics"""
    source_multipliers = {
        'ios_app': 3.0,
        'macos_app': 3.0,
        'web': 2.5,
        'dashboard': 2.0,
        'terminal': 1.5,
        'cron': 1.2,
        'manual': 1.0,
        'test': 0.5
    }
    multiplier = source_multipliers.get(source, 1.0)

    high_value_keywords = ['deploy', 'release', 'fix bug', 'feature', 'refactor', 'optimize', 'security']
    medium_value_keywords = ['review', 'test', 'document', 'update', 'improve']

    prompt_lower = prompt.lower()

    if any(kw in prompt_lower for kw in high_value_keywords):
        base_value = 5.0
    elif any(kw in prompt_lower for kw in medium_value_keywords):
        base_value = 3.0
    else:
        base_value = max(1.0, tokens / 2000)

    estimated_value = base_value * multiplier
    min_value = cost_usd * 2
    return round(max(estimated_value, min_value), 2)

def send_macos_notification(title, message, subtitle=None):
    """Send a macOS notification"""
    try:
        script = f'display notification "{message}"'
        if subtitle:
            script += f' subtitle "{subtitle}"'
        script += f' with title "{title}"'
        subprocess.run(['osascript', '-e', script], check=False, timeout=5)
    except Exception as e:
        print(f"[NOTIFY] Failed: {e}")

# ========== GATEWAY ENDPOINTS ==========

@app.route('/')
def index():
    """Gateway info page"""
    return jsonify({
        'name': 'NEOG Gateway',
        'version': GATEWAY_VERSION,
        'status': 'running',
        'port': PORT,
        'uptime_seconds': int(time.time() - GATEWAY_START_TIME),
        'endpoints': {
            'gateway': ['/health', '/api/gateway/status', '/api/gateway/services'],
            'cc_roi': ['/api/cc/roi/summary', '/api/cc/roi/all-time', '/api/cc/runs', '/api/cc/budget', '/api/cc/health']
        }
    })

@app.route('/health')
def health():
    """Simple health check"""
    return jsonify({'status': 'ok', 'timestamp': datetime.now().isoformat()})

@app.route('/api/gateway/status')
def gateway_status():
    """Detailed gateway status"""
    uptime = int(time.time() - GATEWAY_START_TIME)

    # Check database
    db_ok = False
    db_size = 0
    try:
        if os.path.exists(DB_PATH):
            db_size = os.path.getsize(DB_PATH)
            conn = get_db()
            conn.execute('SELECT 1')
            conn.close()
            db_ok = True
    except:
        pass

    return jsonify({
        'version': GATEWAY_VERSION,
        'port': PORT,
        'uptime_seconds': uptime,
        'uptime_formatted': f'{uptime // 3600}h {(uptime % 3600) // 60}m {uptime % 60}s',
        'started_at': datetime.fromtimestamp(GATEWAY_START_TIME).isoformat(),
        'database': {
            'path': DB_PATH,
            'ok': db_ok,
            'size_bytes': db_size,
            'size_mb': round(db_size / 1024 / 1024, 2)
        }
    })

@app.route('/api/gateway/services')
def gateway_services():
    """List available services and their status"""
    services = [
        {'name': 'CC ROI Tracking', 'status': 'active', 'endpoints': 15},
        {'name': 'Budget Management', 'status': 'active', 'endpoints': 3},
        {'name': 'Guardrails', 'status': 'active', 'endpoints': 2},
    ]
    return jsonify({
        'services': services,
        'total_endpoints': sum(s['endpoints'] for s in services)
    })

# ========== CC RUNS ENDPOINTS ==========

@app.route('/api/cc/run', methods=['POST'])
def create_run():
    """Create a new automation run"""
    try:
        data = request.json or {}
        run_id = data.get('run_id', str(uuid.uuid4()))
        device_ip = request.headers.get('X-Forwarded-For', request.remote_addr)

        conn = get_db()
        c = conn.cursor()
        c.execute('''INSERT INTO cc_runs (run_id, session_id, prompt, source, device_ip, status, model)
                     VALUES (?, ?, ?, ?, ?, ?, ?)''',
                  (run_id,
                   data.get('session_id'),
                   data.get('prompt', ''),
                   data.get('source', 'manual'),
                   device_ip,
                   'running',
                   data.get('model', 'claude-opus-4-5-20251101')))
        conn.commit()
        conn.close()

        socketio.emit('cc_run_created', {
            'run_id': run_id,
            'prompt': data.get('prompt', '')[:100],
            'source': data.get('source', 'manual'),
            'status': 'running'
        })

        return jsonify({'status': 'ok', 'run_id': run_id})

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/run/<run_id>', methods=['GET'])
def get_run(run_id):
    """Get a single run with its events"""
    try:
        conn = get_db()
        c = conn.cursor()

        c.execute('SELECT * FROM cc_runs WHERE run_id = ?', (run_id,))
        row = c.fetchone()
        if not row:
            conn.close()
            return jsonify({'status': 'error', 'message': 'Run not found'}), 404

        run = dict(row)

        # Parse JSON fields
        for field in ['tools_used', 'files_modified', 'tags']:
            if run.get(field):
                try:
                    run[field] = json.loads(run[field])
                except:
                    pass

        # Get events
        c.execute('SELECT * FROM cc_events WHERE run_id = ? ORDER BY created_at ASC', (run_id,))
        events = [dict(r) for r in c.fetchall()]
        run['events'] = events

        conn.close()
        return jsonify(run)

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/run/<run_id>', methods=['PATCH'])
def update_run(run_id):
    """Update an existing run"""
    try:
        data = request.json or {}
        conn = get_db()
        c = conn.cursor()

        updates = []
        params = []
        allowed_fields = ['status', 'input_tokens', 'output_tokens', 'cache_read_tokens',
                          'cost_usd', 'value_generated_usd', 'gate_guardrails_ok',
                          'gate_budget_ok', 'gate_heartbeat_ok', 'halt_reason',
                          'tools_used', 'files_modified', 'error_message']

        for field in allowed_fields:
            if field in data:
                updates.append(f'{field} = ?')
                value = data[field]
                if isinstance(value, (list, dict)):
                    value = json.dumps(value)
                params.append(value)

        # Auto-calculate cost if tokens provided
        if 'input_tokens' in data or 'output_tokens' in data:
            c.execute('SELECT input_tokens, output_tokens, cache_read_tokens, model FROM cc_runs WHERE run_id = ?', (run_id,))
            row = c.fetchone()
            if row:
                input_t = data.get('input_tokens', row[0])
                output_t = data.get('output_tokens', row[1])
                cache_t = data.get('cache_read_tokens', row[2])
                model = row[3]
                cost = calculate_cost(input_t, output_t, cache_t, model)
                updates.append('cost_usd = ?')
                params.append(cost)

        if updates:
            params.append(run_id)
            c.execute(f'UPDATE cc_runs SET {", ".join(updates)} WHERE run_id = ?', params)
            conn.commit()

        conn.close()
        socketio.emit('cc_run_updated', {'run_id': run_id, **data})
        return jsonify({'status': 'ok', 'run_id': run_id})

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/run/<run_id>/complete', methods=['POST'])
def complete_run(run_id):
    """Mark a run as completed"""
    try:
        data = request.json or {}
        conn = get_db()
        c = conn.cursor()

        c.execute('SELECT started_at, model, prompt, source FROM cc_runs WHERE run_id = ?', (run_id,))
        row = c.fetchone()
        if not row:
            conn.close()
            return jsonify({'status': 'error', 'message': 'Run not found'}), 404

        started_at = datetime.fromisoformat(row[0]) if row[0] else datetime.now()
        model = row[1] or 'default'
        prompt = row[2] or ''
        source = row[3] or 'manual'
        completed_at = datetime.now()
        duration_ms = int((completed_at - started_at).total_seconds() * 1000)

        input_tokens = data.get('input_tokens', 0)
        output_tokens = data.get('output_tokens', 0)
        total_tokens = input_tokens + output_tokens
        cost_usd = calculate_cost(input_tokens, output_tokens, 0, model)

        value_generated = data.get('value_generated_usd')
        value_is_estimated = 1
        if value_generated is None or value_generated == 0:
            value_generated = estimate_run_value(prompt, source, cost_usd, total_tokens)
        else:
            value_is_estimated = 0

        status = data.get('status', 'completed')
        c.execute('''UPDATE cc_runs SET status = ?, completed_at = ?, duration_ms = ?,
                     input_tokens = COALESCE(?, input_tokens),
                     output_tokens = COALESCE(?, output_tokens),
                     cost_usd = ?,
                     value_generated_usd = ?,
                     value_is_estimated = ?,
                     error_message = COALESCE(?, error_message)
                     WHERE run_id = ?''',
                  (status, completed_at.isoformat(), duration_ms,
                   input_tokens, output_tokens, cost_usd,
                   value_generated, value_is_estimated, data.get('error_message'), run_id))
        conn.commit()

        # Check budget
        today = datetime.now().strftime('%Y-%m-%d')
        c.execute('SELECT COALESCE(SUM(cost_usd), 0) FROM cc_runs WHERE DATE(started_at) = ?', (today,))
        spent_today = c.fetchone()[0]

        c.execute('SELECT daily_limit_usd, alert_threshold_pct FROM budget_config WHERE name = "default"')
        budget_row = c.fetchone()
        daily_limit = budget_row[0] if budget_row else 10.00
        alert_pct = budget_row[1] if budget_row else 80
        budget_used_pct = (spent_today / daily_limit * 100) if daily_limit > 0 else 0

        conn.close()

        # Send budget alerts
        if budget_used_pct >= 100:
            send_macos_notification("CC Budget EXCEEDED", f"${spent_today:.2f}/${daily_limit:.2f}")
        elif budget_used_pct >= alert_pct:
            send_macos_notification("CC Budget Warning", f"{budget_used_pct:.0f}% used")

        socketio.emit('cc_run_completed', {
            'run_id': run_id,
            'status': status,
            'duration_ms': duration_ms,
            'cost_usd': cost_usd,
            'budget_used_pct': round(budget_used_pct, 1)
        })

        return jsonify({
            'status': 'ok',
            'run_id': run_id,
            'duration_ms': duration_ms,
            'cost_usd': cost_usd,
            'budget_used_pct': round(budget_used_pct, 1)
        })

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/run/<run_id>/value', methods=['POST'])
def update_run_value(run_id):
    """Update the real value of a completed run"""
    try:
        data = request.json or {}
        value_usd = data.get('value_usd', 0)

        conn = get_db()
        c = conn.cursor()
        c.execute('''UPDATE cc_runs SET value_generated_usd = ?, value_is_estimated = 0
                     WHERE run_id = ?''', (value_usd, run_id))
        conn.commit()

        c.execute('SELECT cost_usd, value_generated_usd FROM cc_runs WHERE run_id = ?', (run_id,))
        row = c.fetchone()
        conn.close()

        if row:
            return jsonify({
                'status': 'ok',
                'run_id': run_id,
                'cost_usd': row[0],
                'value_usd': row[1],
                'roi_pct': round((row[1] / row[0] - 1) * 100, 1) if row[0] > 0 else 0
            })
        return jsonify({'status': 'error', 'message': 'Run not found'}), 404

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/runs', methods=['GET'])
def get_runs():
    """Get runs with optional filters"""
    try:
        limit = request.args.get('limit', 50, type=int)
        offset = request.args.get('offset', 0, type=int)
        status = request.args.get('status')
        source = request.args.get('source')

        conn = get_db()
        c = conn.cursor()

        query = 'SELECT * FROM cc_runs WHERE 1=1'
        params = []

        if status:
            query += ' AND status = ?'
            params.append(status)
        if source:
            query += ' AND source = ?'
            params.append(source)

        query += ' ORDER BY started_at DESC LIMIT ? OFFSET ?'
        params.extend([limit, offset])

        c.execute(query, params)
        runs = [dict(row) for row in c.fetchall()]

        # Parse JSON fields
        for run in runs:
            for field in ['tools_used', 'files_modified', 'tags']:
                if run.get(field):
                    try:
                        run[field] = json.loads(run[field])
                    except:
                        pass

        conn.close()
        return jsonify(runs)

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

# ========== ROI ENDPOINTS ==========

@app.route('/api/cc/roi/summary', methods=['GET'])
def get_roi_summary():
    """Get today's ROI summary"""
    try:
        conn = get_db()
        c = conn.cursor()
        today = datetime.now().strftime('%Y-%m-%d')

        c.execute('''SELECT
                        COALESCE(SUM(cost_usd), 0) as total_cost,
                        COALESCE(SUM(value_generated_usd), 0) as total_revenue,
                        COALESCE(SUM(input_tokens), 0) as input_tokens,
                        COALESCE(SUM(output_tokens), 0) as output_tokens,
                        COUNT(*) as run_count,
                        COUNT(CASE WHEN status = 'running' THEN 1 END) as active_runs,
                        COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed_runs,
                        COUNT(CASE WHEN status IN ('failed', 'halted') THEN 1 END) as failed_runs
                     FROM cc_runs WHERE DATE(started_at) = ?''', (today,))

        row = c.fetchone()
        total_cost = row[0]
        total_revenue = row[1]

        c.execute('SELECT daily_limit_usd FROM budget_config WHERE name = "default"')
        budget_row = c.fetchone()
        daily_limit = budget_row[0] if budget_row else 10.00
        budget_pct = (total_cost / daily_limit * 100) if daily_limit > 0 else 0

        conn.close()

        profit = total_revenue - total_cost
        roi_pct = ((total_revenue - total_cost) / total_cost * 100) if total_cost > 0 else 0

        return jsonify({
            'today': today,
            'cost_usd': round(total_cost, 4),
            'revenue_usd': round(total_revenue, 2),
            'profit_usd': round(profit, 2),
            'roi_pct': round(roi_pct, 1),
            'tokens_input': row[2],
            'tokens_output': row[3],
            'runs_total': row[4],
            'runs_active': row[5],
            'runs_completed': row[6],
            'runs_failed': row[7],
            'budget_daily_usd': daily_limit,
            'budget_used_pct': round(budget_pct, 1)
        })

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/roi/all-time', methods=['GET'])
def get_roi_all_time():
    """Get all-time ROI statistics"""
    try:
        conn = get_db()
        c = conn.cursor()

        c.execute('''SELECT
                        COALESCE(SUM(cost_usd), 0) as total_cost,
                        COALESCE(SUM(value_generated_usd), 0) as total_revenue,
                        COALESCE(SUM(input_tokens), 0) as input_tokens,
                        COALESCE(SUM(output_tokens), 0) as output_tokens,
                        COUNT(*) as total_runs,
                        COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed_runs,
                        COUNT(CASE WHEN status IN ('failed', 'halted') THEN 1 END) as failed_runs,
                        MIN(DATE(started_at)) as first_run,
                        MAX(DATE(started_at)) as last_run
                     FROM cc_runs''')

        row = c.fetchone()
        total_cost = row[0]
        total_revenue = row[1]
        total_runs = row[4]
        completed_runs = row[5]

        c.execute('SELECT COUNT(DISTINCT DATE(started_at)) FROM cc_runs')
        active_days = c.fetchone()[0]

        conn.close()

        profit = total_revenue - total_cost
        roi_pct = ((total_revenue - total_cost) / total_cost * 100) if total_cost > 0 else 0
        success_rate = (completed_runs / total_runs * 100) if total_runs > 0 else 0

        return jsonify({
            'cost_usd': round(total_cost, 4),
            'revenue_usd': round(total_revenue, 2),
            'profit_usd': round(profit, 2),
            'roi_pct': round(roi_pct, 1),
            'tokens_input': row[2],
            'tokens_output': row[3],
            'runs_total': total_runs,
            'runs_completed': completed_runs,
            'runs_failed': row[6],
            'active_days': active_days,
            'first_run_date': row[7],
            'last_run_date': row[8],
            'success_rate_pct': round(success_rate, 1)
        })

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/roi/daily', methods=['GET'])
def get_roi_daily():
    """Get daily breakdown"""
    try:
        days = request.args.get('days', 30, type=int)
        conn = get_db()
        c = conn.cursor()

        c.execute('''SELECT
                        DATE(started_at) as date,
                        COALESCE(SUM(cost_usd), 0) as cost,
                        COALESCE(SUM(value_generated_usd), 0) as revenue,
                        COUNT(*) as runs
                     FROM cc_runs
                     WHERE started_at >= DATE('now', ?)
                     GROUP BY DATE(started_at)
                     ORDER BY date DESC''', (f'-{days} days',))

        daily = [dict(row) for row in c.fetchall()]
        conn.close()
        return jsonify(daily)

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

# ========== BUDGET ENDPOINTS ==========

@app.route('/api/cc/budget', methods=['GET'])
def get_budget():
    """Get budget configuration and usage"""
    try:
        conn = get_db()
        c = conn.cursor()

        # Get budget config
        c.execute('SELECT daily_limit_usd, monthly_limit_usd, max_tokens_per_run, max_cost_per_run_usd, alert_threshold_pct, is_active FROM budget_config WHERE name = "default"')
        row = c.fetchone()

        if row:
            daily_limit = row[0]
            monthly_limit = row[1]
        else:
            daily_limit = 10.0
            monthly_limit = 200.0

        # Today's usage
        today = datetime.now().strftime('%Y-%m-%d')
        c.execute('SELECT COALESCE(SUM(cost_usd), 0) FROM cc_runs WHERE DATE(started_at) = ?', (today,))
        spent_today = c.fetchone()[0]

        # This month's usage
        month_start = datetime.now().strftime('%Y-%m-01')
        c.execute('SELECT COALESCE(SUM(cost_usd), 0) FROM cc_runs WHERE started_at >= ?', (month_start,))
        spent_month = c.fetchone()[0]

        conn.close()

        return jsonify({
            'daily_limit_usd': daily_limit,
            'monthly_limit_usd': monthly_limit,
            'max_tokens_per_run': row[2] if row else 100000,
            'max_cost_per_run_usd': row[3] if row else 2.0,
            'alert_threshold_pct': row[4] if row else 80,
            'is_active': row[5] if row else True,
            'spent_today': round(spent_today, 4),
            'spent_month': round(spent_month, 4),
            'daily_remaining': round(daily_limit - spent_today, 4),
            'monthly_remaining': round(monthly_limit - spent_month, 4)
        })

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/budget', methods=['POST'])
def update_budget():
    """Update budget configuration"""
    try:
        data = request.json or {}
        conn = get_db()
        c = conn.cursor()

        updates = []
        params = []
        for field in ['daily_limit_usd', 'monthly_limit_usd', 'max_tokens_per_run', 'max_cost_per_run_usd', 'alert_threshold_pct', 'is_active']:
            if field in data:
                updates.append(f'{field} = ?')
                params.append(data[field])

        if updates:
            updates.append('updated_at = ?')
            params.append(datetime.now().isoformat())
            params.append('default')
            c.execute(f'UPDATE budget_config SET {", ".join(updates)} WHERE name = ?', params)
            conn.commit()

        conn.close()
        return jsonify({'status': 'ok'})

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

# ========== HEALTH & GATES ==========

@app.route('/api/cc/health', methods=['GET'])
def get_gate_health():
    """Get 3-gate health status"""
    try:
        conn = get_db()
        c = conn.cursor()

        # Guardrails check
        c.execute('SELECT COUNT(*) FROM guardrails WHERE is_active = 1')
        guardrails_count = c.fetchone()[0]
        guardrails_ok = guardrails_count > 0

        # Budget check
        today = datetime.now().strftime('%Y-%m-%d')
        c.execute('SELECT COALESCE(SUM(cost_usd), 0) FROM cc_runs WHERE DATE(started_at) = ?', (today,))
        spent_today = c.fetchone()[0]

        c.execute('SELECT daily_limit_usd FROM budget_config WHERE name = "default"')
        row = c.fetchone()
        daily_limit = row[0] if row else 10.0
        budget_ok = spent_today < daily_limit

        # Heartbeat (gateway is running)
        heartbeat_ok = True

        conn.close()

        overall = 'healthy' if all([guardrails_ok, budget_ok, heartbeat_ok]) else 'degraded'

        return jsonify({
            'overall': overall,
            'guardrails': {'ok': guardrails_ok, 'active_rules': guardrails_count},
            'budget': {'ok': budget_ok, 'spent_today': round(spent_today, 4), 'daily_limit': daily_limit},
            'heartbeat': {'ok': heartbeat_ok, 'uptime': int(time.time() - GATEWAY_START_TIME)},
            'timestamp': datetime.now().isoformat()
        })

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/guardrails', methods=['GET'])
def get_guardrails():
    """Get active guardrails"""
    try:
        conn = get_db()
        c = conn.cursor()
        c.execute('SELECT * FROM guardrails WHERE is_active = 1')
        guardrails = [dict(row) for row in c.fetchall()]
        conn.close()
        return jsonify(guardrails)

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/api/cc/gate/check', methods=['POST'])
def gate_check():
    """Pre-flight safety gate check"""
    try:
        data = request.json or {}
        prompt = data.get('prompt', '')
        estimated_cost = data.get('estimated_cost', 0)

        conn = get_db()
        c = conn.cursor()

        # Check guardrails
        c.execute('SELECT pattern, action FROM guardrails WHERE is_active = 1')
        blocked = []
        warned = []
        for pattern, action in c.fetchall():
            if pattern.lower() in prompt.lower():
                if action == 'block':
                    blocked.append(pattern)
                else:
                    warned.append(pattern)

        # Check budget
        today = datetime.now().strftime('%Y-%m-%d')
        c.execute('SELECT COALESCE(SUM(cost_usd), 0) FROM cc_runs WHERE DATE(started_at) = ?', (today,))
        spent_today = c.fetchone()[0]

        c.execute('SELECT daily_limit_usd, max_cost_per_run_usd FROM budget_config WHERE name = "default"')
        row = c.fetchone()
        daily_limit = row[0] if row else 10.0
        max_per_run = row[1] if row else 2.0

        budget_ok = (spent_today + estimated_cost) <= daily_limit
        cost_ok = estimated_cost <= max_per_run

        conn.close()

        can_proceed = len(blocked) == 0 and budget_ok and cost_ok

        return jsonify({
            'can_proceed': can_proceed,
            'guardrails': {
                'blocked': blocked,
                'warned': warned,
                'ok': len(blocked) == 0
            },
            'budget': {
                'ok': budget_ok and cost_ok,
                'spent_today': round(spent_today, 4),
                'daily_limit': daily_limit,
                'remaining': round(daily_limit - spent_today, 4),
                'estimated_cost': estimated_cost
            }
        })

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

# ========== EVENTS ==========

@app.route('/api/cc/event', methods=['POST'])
def log_event():
    """Log an event for a run"""
    try:
        data = request.json or {}
        run_id = data.get('run_id')
        if not run_id:
            return jsonify({'status': 'error', 'message': 'run_id required'}), 400

        input_tokens = data.get('input_tokens', 0)
        output_tokens = data.get('output_tokens', 0)
        cost = calculate_cost(input_tokens, output_tokens)

        conn = get_db()
        c = conn.cursor()
        c.execute('''INSERT INTO cc_events (run_id, event_type, event_subtype,
                     input_tokens, output_tokens, cost_usd, duration_ms, success)
                     VALUES (?, ?, ?, ?, ?, ?, ?, ?)''',
                  (run_id,
                   data.get('event_type', 'unknown'),
                   data.get('event_subtype'),
                   input_tokens, output_tokens, cost,
                   data.get('duration_ms'),
                   data.get('success', True)))

        event_id = c.lastrowid

        # Update run totals
        c.execute('''UPDATE cc_runs SET
                     input_tokens = input_tokens + ?,
                     output_tokens = output_tokens + ?,
                     cost_usd = cost_usd + ?
                     WHERE run_id = ?''',
                  (input_tokens, output_tokens, cost, run_id))

        conn.commit()
        conn.close()

        socketio.emit('cc_event', {'id': event_id, 'run_id': run_id, 'cost_usd': cost})
        return jsonify({'status': 'ok', 'event_id': event_id, 'cost_usd': cost})

    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500

# ========== WEBSOCKET ==========

@socketio.on('connect')
def handle_connect():
    print(f"[WS] Client connected: {request.sid}")
    emit('connected', {'message': 'Connected to NEOG Gateway', 'version': GATEWAY_VERSION})

@socketio.on('disconnect')
def handle_disconnect():
    print(f"[WS] Client disconnected: {request.sid}")

# ========== MAIN ==========

if __name__ == '__main__':
    print(f"""
╔══════════════════════════════════════════════════════════════╗
║                     NEOG Gateway v{GATEWAY_VERSION}                       ║
║              Unified API for NEOG Command                    ║
╠══════════════════════════════════════════════════════════════╣
║  Port: {PORT}                                                   ║
║  Database: {DB_PATH:<47} ║
║  Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S'):<47} ║
╚══════════════════════════════════════════════════════════════╝
    """)

    init_db()
    socketio.run(app, host='0.0.0.0', port=PORT, debug=False, allow_unsafe_werkzeug=True)
