#!/usr/bin/env python3
"""
Scoreboard Engine - Story 2
===========================
Real-time scoring, leaderboards, and competition tracking.
Uses Elestio Core PostgreSQL (RULE 6: No SQLite).

Scoring Rules:
- Discovery: +10 points
- Implementation: +25 points
- Documentation: +5 points
- Genesis Integration: +50 points
- Penalty (Missed by AUDIT): -30 points
- Penalty (Broken implementation): -20 points
- Bonus (First to implement): +15 points
- Bonus (Novel combination): +40 points
"""

import sys
import json
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
from dataclasses import dataclass

# Elestio PostgreSQL config
sys.path.append('/mnt/e/genesis-system/data/genesis-memory')
from elestio_config import PostgresConfig
import psycopg2
from psycopg2.extras import RealDictCursor


@dataclass
class ScoreConfig:
    """Scoring configuration."""
    discovery: int = 10
    implementation: int = 25
    documentation: int = 5
    integration: int = 50
    penalty_missed: int = -30
    penalty_broken: int = -20
    bonus_first: int = 15
    bonus_novel: int = 40


@dataclass
class AgentScore:
    """Individual agent scoring."""
    agent_id: str
    team: str  # 'scout' or 'audit'
    discoveries: int = 0
    implementations: int = 0
    documentations: int = 0
    integrations: int = 0
    penalties: int = 0
    bonuses: int = 0
    total_points: int = 0


@dataclass
class TeamScore:
    """Team scoring summary."""
    team: str
    agents: List[str]
    discoveries: int = 0
    implementations: int = 0
    gaps_found: int = 0
    bugs_found: int = 0
    total_points: int = 0


class Scoreboard:
    """
    Competitive scoreboard for capability discovery swarm.
    Uses Elestio Core PostgreSQL.

    Usage:
        scoreboard = Scoreboard()

        # Award points
        scoreboard.award_discovery("scout_s1", "cap_001")
        scoreboard.award_implementation("scout_s5", "cap_001")

        # Apply penalties
        scoreboard.apply_penalty("scout_s2", "missed", "cap_002")

        # Get leaderboard
        leaders = scoreboard.get_agent_leaderboard()
        team_scores = scoreboard.get_team_scores()

        # Generate 4am report
        report = scoreboard.generate_nightly_report()
    """

    def __init__(self, config: Optional[ScoreConfig] = None):
        self.conn_params = PostgresConfig.get_connection_params()
        self.config = config or ScoreConfig()
        self._init_db()

    def _get_conn(self):
        """Get a PostgreSQL connection."""
        return psycopg2.connect(**self.conn_params)

    def _init_db(self):
        """Initialize scoreboard database."""
        with self._get_conn() as conn:
            with conn.cursor() as cur:
                cur.execute("""
                    CREATE TABLE IF NOT EXISTS swarm_agents (
                        id TEXT PRIMARY KEY,
                        team TEXT NOT NULL,
                        name TEXT,
                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                    )
                """)

                cur.execute("""
                    CREATE TABLE IF NOT EXISTS score_events (
                        id SERIAL PRIMARY KEY,
                        agent_id TEXT NOT NULL REFERENCES swarm_agents(id),
                        event_type TEXT NOT NULL,
                        points INTEGER NOT NULL,
                        capability_id TEXT,
                        details TEXT,
                        night_date TEXT,
                        timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                    )
                """)

                cur.execute("""
                    CREATE TABLE IF NOT EXISTS nightly_summaries (
                        id SERIAL PRIMARY KEY,
                        night_date TEXT UNIQUE,
                        scout_points INTEGER,
                        audit_points INTEGER,
                        capabilities_discovered INTEGER,
                        capabilities_implemented INTEGER,
                        gaps_found INTEGER,
                        report_json JSONB,
                        generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                    )
                """)

                cur.execute("CREATE INDEX IF NOT EXISTS idx_events_agent ON score_events(agent_id)")
                cur.execute("CREATE INDEX IF NOT EXISTS idx_events_night ON score_events(night_date)")
            conn.commit()

    def register_agent(self, agent_id: str, team: str, name: str = ""):
        """Register an agent."""
        with self._get_conn() as conn:
            with conn.cursor() as cur:
                cur.execute("""
                    INSERT INTO swarm_agents (id, team, name)
                    VALUES (%s, %s, %s)
                    ON CONFLICT (id) DO UPDATE SET team = %s, name = %s
                """, (agent_id, team, name or agent_id, team, name or agent_id))
            conn.commit()

    def _get_night_date(self) -> str:
        """Get current night date (switches at 4am AEST)."""
        # For scoring purposes, the "night" runs from 8pm to 4am
        # After 4am, it's a new night
        now = datetime.now()
        if now.hour < 4:
            # Still part of previous night
            return (now - timedelta(days=1)).strftime("%Y-%m-%d")
        return now.strftime("%Y-%m-%d")

    def _record_event(self, agent_id: str, event_type: str, points: int,
                      capability_id: str = "", details: str = ""):
        """Record a scoring event."""
        with self._get_conn() as conn:
            with conn.cursor() as cur:
                cur.execute("""
                    INSERT INTO score_events (agent_id, event_type, points, capability_id, details, night_date)
                    VALUES (%s, %s, %s, %s, %s, %s)
                """, (agent_id, event_type, points, capability_id, details, self._get_night_date()))
            conn.commit()

    # === Award Methods ===

    def award_discovery(self, agent_id: str, capability_id: str, details: str = "") -> int:
        """Award points for discovering a capability."""
        points = self.config.discovery
        self._record_event(agent_id, "discovery", points, capability_id, details)
        return points

    def award_implementation(self, agent_id: str, capability_id: str,
                            is_first: bool = False, details: str = "") -> int:
        """Award points for implementing a capability."""
        points = self.config.implementation
        self._record_event(agent_id, "implementation", points, capability_id, details)

        if is_first:
            bonus = self.config.bonus_first
            self._record_event(agent_id, "bonus_first", bonus, capability_id, "First to implement")
            points += bonus

        return points

    def award_documentation(self, agent_id: str, capability_id: str, details: str = "") -> int:
        """Award points for documenting a capability."""
        points = self.config.documentation
        self._record_event(agent_id, "documentation", points, capability_id, details)
        return points

    def award_integration(self, agent_id: str, capability_id: str, details: str = "") -> int:
        """Award points for Genesis integration."""
        points = self.config.integration
        self._record_event(agent_id, "integration", points, capability_id, details)
        return points

    def award_novel_combination(self, agent_id: str, capability_id: str, details: str = "") -> int:
        """Award bonus for novel capability combination."""
        points = self.config.bonus_novel
        self._record_event(agent_id, "bonus_novel", points, capability_id, details)
        return points

    def award_gap_found(self, agent_id: str, capability_id: str, details: str = "") -> int:
        """Award points to AUDIT for finding a gap (applies penalty to SCOUT)."""
        # AUDIT gets recognition, SCOUT gets penalty
        self._record_event(agent_id, "gap_found", 0, capability_id, details)
        # The actual penalty is applied via apply_penalty
        return 0

    def award_bug_found(self, agent_id: str, capability_id: str, details: str = "") -> int:
        """Award points to AUDIT for finding a bug."""
        self._record_event(agent_id, "bug_found", 0, capability_id, details)
        return 0

    # === Penalty Methods ===

    def apply_penalty(self, agent_id: str, penalty_type: str,
                      capability_id: str = "", details: str = "") -> int:
        """Apply a penalty."""
        if penalty_type == "missed":
            points = self.config.penalty_missed
        elif penalty_type == "broken":
            points = self.config.penalty_broken
        else:
            points = 0

        self._record_event(agent_id, f"penalty_{penalty_type}", points, capability_id, details)
        return points

    # === Query Methods ===

    def get_agent_score(self, agent_id: str, night_date: str = None) -> AgentScore:
        """Get an agent's score."""
        night_date = night_date or self._get_night_date()

        with self._get_conn() as conn:
            with conn.cursor(cursor_factory=RealDictCursor) as cur:
                # Get agent info
                cur.execute("SELECT * FROM swarm_agents WHERE id = %s", (agent_id,))
                agent = cur.fetchone()
                if not agent:
                    return AgentScore(agent_id=agent_id, team="unknown")

                # Get events for this night
                cur.execute("""
                    SELECT event_type, SUM(points) as total
                    FROM score_events
                    WHERE agent_id = %s AND night_date = %s
                    GROUP BY event_type
                """, (agent_id, night_date))
                events = cur.fetchall()

                score = AgentScore(agent_id=agent_id, team=agent['team'])

                for event in events:
                    etype = event['event_type']
                    total = event['total'] or 0

                    if etype == 'discovery':
                        cur.execute(
                            "SELECT COUNT(*) FROM score_events WHERE agent_id = %s AND night_date = %s AND event_type = 'discovery'",
                            (agent_id, night_date)
                        )
                        score.discoveries = cur.fetchone()[0]
                        score.total_points += total
                    elif etype == 'implementation':
                        cur.execute(
                            "SELECT COUNT(*) FROM score_events WHERE agent_id = %s AND night_date = %s AND event_type = 'implementation'",
                            (agent_id, night_date)
                        )
                        score.implementations = cur.fetchone()[0]
                        score.total_points += total
                    elif etype == 'documentation':
                        cur.execute(
                            "SELECT COUNT(*) FROM score_events WHERE agent_id = %s AND night_date = %s AND event_type = 'documentation'",
                            (agent_id, night_date)
                        )
                        score.documentations = cur.fetchone()[0]
                        score.total_points += total
                    elif etype == 'integration':
                        cur.execute(
                            "SELECT COUNT(*) FROM score_events WHERE agent_id = %s AND night_date = %s AND event_type = 'integration'",
                            (agent_id, night_date)
                        )
                        score.integrations = cur.fetchone()[0]
                        score.total_points += total
                    elif etype.startswith('bonus'):
                        score.bonuses += total
                        score.total_points += total
                    elif etype.startswith('penalty'):
                        score.penalties += abs(total)
                        score.total_points += total  # Already negative

                return score

    def get_agent_leaderboard(self, night_date: str = None, limit: int = 10) -> List[Dict]:
        """Get agent leaderboard sorted by points."""
        night_date = night_date or self._get_night_date()

        with self._get_conn() as conn:
            with conn.cursor(cursor_factory=RealDictCursor) as cur:
                cur.execute("""
                    SELECT
                        a.id as agent_id,
                        a.team,
                        a.name,
                        COALESCE(SUM(e.points), 0) as total_points,
                        COUNT(CASE WHEN e.event_type = 'discovery' THEN 1 END) as discoveries,
                        COUNT(CASE WHEN e.event_type = 'implementation' THEN 1 END) as implementations
                    FROM swarm_agents a
                    LEFT JOIN score_events e ON a.id = e.agent_id AND e.night_date = %s
                    GROUP BY a.id, a.team, a.name
                    ORDER BY total_points DESC
                    LIMIT %s
                """, (night_date, limit))

                return [dict(row) for row in cur.fetchall()]

    def get_team_scores(self, night_date: str = None) -> Dict[str, TeamScore]:
        """Get team scores."""
        night_date = night_date or self._get_night_date()

        with self._get_conn() as conn:
            with conn.cursor(cursor_factory=RealDictCursor) as cur:
                teams = {}
                for team in ['scout', 'audit']:
                    cur.execute("SELECT id FROM swarm_agents WHERE team = %s", (team,))
                    agents = cur.fetchall()
                    agent_ids = [a['id'] for a in agents]

                    if not agent_ids:
                        teams[team] = TeamScore(team=team, agents=[])
                        continue

                    placeholders = ','.join(['%s'] * len(agent_ids))
                    cur.execute(f"""
                        SELECT
                            COALESCE(SUM(points), 0) as total_points,
                            COUNT(CASE WHEN event_type = 'discovery' THEN 1 END) as discoveries,
                            COUNT(CASE WHEN event_type = 'implementation' THEN 1 END) as implementations,
                            COUNT(CASE WHEN event_type = 'gap_found' THEN 1 END) as gaps_found,
                            COUNT(CASE WHEN event_type = 'bug_found' THEN 1 END) as bugs_found
                        FROM score_events
                        WHERE agent_id IN ({placeholders}) AND night_date = %s
                    """, agent_ids + [night_date])
                    events = cur.fetchone()

                    teams[team] = TeamScore(
                        team=team,
                        agents=agent_ids,
                        discoveries=events['discoveries'] or 0,
                        implementations=events['implementations'] or 0,
                        gaps_found=events['gaps_found'] or 0,
                        bugs_found=events['bugs_found'] or 0,
                        total_points=events['total_points'] or 0
                    )

                return teams

    def get_recent_activity(self, limit: int = 20) -> List[Dict]:
        """Get recent scoring activity."""
        with self._get_conn() as conn:
            with conn.cursor(cursor_factory=RealDictCursor) as cur:
                cur.execute("""
                    SELECT e.*, a.team, a.name as agent_name
                    FROM score_events e
                    JOIN swarm_agents a ON e.agent_id = a.id
                    ORDER BY e.timestamp DESC
                    LIMIT %s
                """, (limit,))
                return [dict(row) for row in cur.fetchall()]

    # === Reporting ===

    def generate_nightly_report(self, night_date: str = None) -> str:
        """Generate the 4am nightly report."""
        night_date = night_date or self._get_night_date()
        teams = self.get_team_scores(night_date)
        leaderboard = self.get_agent_leaderboard(night_date, limit=10)
        activity = self.get_recent_activity(limit=10)

        scout = teams.get('scout', TeamScore(team='scout', agents=[]))
        audit = teams.get('audit', TeamScore(team='audit', agents=[]))

        report = []
        report.append("=" * 66)
        report.append("       CLAUDE CODE CAPABILITY SWARM - SCOREBOARD")
        report.append(f"                  Night of {night_date}")
        report.append("=" * 66)
        report.append("")
        report.append(f"  TEAM SCOUT                          TEAM AUDIT")
        report.append(f"  ==========                          ==========")
        report.append(f"  Discovered: {scout.discoveries:<10}               Gaps Found: {audit.gaps_found}")
        report.append(f"  Implemented: {scout.implementations:<9}               Bugs Found: {audit.bugs_found}")
        report.append(f"  ")
        report.append(f"  POINTS: {scout.total_points:<14}               POINTS: {audit.total_points}")
        report.append("")
        report.append("-" * 66)
        report.append("  AGENT LEADERBOARD")
        report.append("  -----------------")

        for i, agent in enumerate(leaderboard[:5], 1):
            name = agent['name'] or agent['agent_id']
            points = agent['total_points']
            report.append(f"  {i}. {name:<30} +{points} pts")

        report.append("")
        report.append("-" * 66)
        report.append("  RECENT ACTIVITY")
        report.append("  ---------------")

        for event in activity[:5]:
            ts = str(event['timestamp'])
            time = ts.split(' ')[1][:5] if ' ' in ts else ts[-8:-3]
            agent = event['agent_name'] or event['agent_id']
            etype = event['event_type'].replace('_', ' ')
            points = event['points']
            sign = '+' if points >= 0 else ''
            report.append(f"  {time}  {agent}: {etype:<20} {sign}{points} pts")

        report.append("")
        report.append("=" * 66)

        # Store summary
        self._store_nightly_summary(night_date, scout, audit, "\n".join(report))

        return "\n".join(report)

    def _store_nightly_summary(self, night_date: str, scout: TeamScore, audit: TeamScore, report: str):
        """Store nightly summary in database."""
        with self._get_conn() as conn:
            with conn.cursor() as cur:
                cur.execute("""
                    INSERT INTO nightly_summaries
                    (night_date, scout_points, audit_points, capabilities_discovered,
                     capabilities_implemented, gaps_found, report_json)
                    VALUES (%s, %s, %s, %s, %s, %s, %s)
                    ON CONFLICT (night_date) DO UPDATE SET
                        scout_points = EXCLUDED.scout_points,
                        audit_points = EXCLUDED.audit_points,
                        capabilities_discovered = EXCLUDED.capabilities_discovered,
                        capabilities_implemented = EXCLUDED.capabilities_implemented,
                        gaps_found = EXCLUDED.gaps_found,
                        report_json = EXCLUDED.report_json
                """, (
                    night_date, scout.total_points, audit.total_points,
                    scout.discoveries, scout.implementations, audit.gaps_found,
                    json.dumps({"report_text": report})
                ))
            conn.commit()

    def get_ascii_scoreboard(self) -> str:
        """Get a compact ASCII scoreboard for display."""
        teams = self.get_team_scores()
        leaderboard = self.get_agent_leaderboard(limit=5)

        scout = teams.get('scout', TeamScore(team='scout', agents=[]))
        audit = teams.get('audit', TeamScore(team='audit', agents=[]))

        total_caps = scout.discoveries
        implemented = scout.implementations
        progress = (implemented / total_caps * 100) if total_caps > 0 else 0
        progress_bar = "█" * int(progress / 5) + "░" * (20 - int(progress / 5))

        lines = [
            f"SCOUT: {scout.total_points} pts | AUDIT: {audit.total_points} pts",
            f"Progress: [{progress_bar}] {progress:.0f}% ({implemented}/{total_caps})",
            f"Top: {leaderboard[0]['agent_id'] if leaderboard else 'N/A'}"
        ]

        return "\n".join(lines)


# Quick test
if __name__ == "__main__":
    print("Testing PostgreSQL Scoreboard (Elestio Core)...")
    scoreboard = Scoreboard()

    # Register test agents
    scoreboard.register_agent("scout_s1", "scout", "Docs Crawler")
    scoreboard.register_agent("scout_s5", "scout", "Implementer")
    scoreboard.register_agent("audit_a1", "audit", "Gap Finder")

    # Award some points
    scoreboard.award_discovery("scout_s1", "cap_001", "Found parallel tools")
    scoreboard.award_implementation("scout_s5", "cap_001", is_first=True)
    scoreboard.award_integration("scout_s5", "cap_001")

    # Generate report
    print(scoreboard.generate_nightly_report())
    print("\n  Test complete - PostgreSQL connection verified")
