<template>
  <div class="leaderboard light">
    <div class="header glass light">
      <div class="header-left">
        <button class="btn-back" @click="goHome" title="Volver al inicio">
          ← <span class="label">Inicio</span>
        </button>
        <h1><span class="emoji">📈</span> Leaderboard</h1>
      </div>
      <div class="actions">
        <button class="btn-collapse" @click="filtersCollapsed = !filtersCollapsed" :title="filtersCollapsed ? 'Mostrar filtros' : 'Ocultar filtros'">
          <span class="collapse-icon" :class="{ rotated: filtersCollapsed }">▼</span>
          <span class="collapse-text">{{ filtersCollapsed ? 'Mostrar filtros' : 'Ocultar filtros' }}</span>
        </button>
        <button class="btn" @click="downloadJSON" :disabled="loading" title="Descargar datos actuales como JSON">
          📊 <span class="label">JSON</span>
        </button>
      </div>
    </div>

    <div class="filters-section" :class="{ collapsed: filtersCollapsed }">
      
      <Transition name="filters-slide">
        <div v-if="!filtersCollapsed" class="filters-content">
          <DataSourceSelector v-model="eventFilters.dataSource.value" />

          <EventFilters 
            :round-filter="eventFilters.roundFilter.value"
            :game-filter="eventFilters.gameFilter.value"
            :has-active-filters="eventFilters.hasActiveFilters.value"
            :filter-summary="eventFilters.filterSummary.value"
            @update:round-filter="eventFilters.roundFilter.value = $event"
            @update:game-filter="eventFilters.gameFilter.value = $event"
            @reset-filters="eventFilters.resetFilters"
          />
        </div>
      </Transition>
    </div>

    <Transition name="filters-slide">
      <div v-if="!filtersCollapsed" class="controls glass light">
        <div class="legend">
          <span class="key global"></span> Global
          <span class="sep">·</span>
          <span class="key player" v-if="selectedUuid"></span> Jugador
          <span class="sep" v-if="selectedRoomId">·</span>
          <span class="key room" v-if="selectedRoomId"></span> Sala
        </div>
        <div class="player-chips">
          <div class="search-controls">
            <input class="search" v-model="search" placeholder="Buscar jugador…" />
            <div class="pagination compact" v-if="pageCount > 1">
              <button class="pg-btn compact" @click="prevPage" :disabled="page <= 1">‹</button>
              <span class="pg-ind">{{ page }}/{{ pageCount }}</span>
              <button class="pg-btn compact" @click="nextPage" :disabled="page >= pageCount">›</button>
            </div>
          </div>
          <div class="chips">
            <button
              v-for="p in playersPage"
              :key="p.uuid"
              class="chip"
              :class="{ active: p.uuid === selectedUuid }"
              @click="selectPlayer(p.uuid)"
              :title="p.uuid"
              :style="{ '--primary': p.color || '#667eea' } as any"
            >
              <span class="avatar">{{ initials(p.name) }}</span>
              <span class="label">{{ p.name || 'Jugador' }}</span>
            </button>
            <button v-if="selectedUuid" class="chip clear" @click="clearPlayer">Quitar selección</button>
          </div>
        </div>
        
        <!-- Room Filter Section -->
        <div class="room-chips">
          <div class="search-controls">
            <input class="search" v-model="roomSearch" placeholder="Buscar sala…" />
            <div class="pagination compact" v-if="roomPageCount > 1">
              <button class="pg-btn compact" @click="prevRoomPage" :disabled="roomPage <= 1">‹</button>
              <span class="pg-ind">{{ roomPage }}/{{ roomPageCount }}</span>
              <button class="pg-btn compact" @click="nextRoomPage" :disabled="roomPage >= roomPageCount">›</button>
            </div>
          </div>
          <div class="chips">
            <button
              v-for="r in roomsPage"
              :key="r.roomId"
              class="chip room-chip"
              :class="{ active: r.roomId === selectedRoomId }"
              @click="selectRoom(r.roomId)"
              :title="`Sala: ${r.roomId} (${r.playerCount || 0} jugadores)`"
            >
              <span class="avatar">🏠</span>
              <span class="label">{{ r.name }}</span>
              <span class="count" v-if="r.playerCount">{{ r.playerCount }}</span>
            </button>
            <button v-if="selectedRoomId" class="chip clear" @click="clearRoom">Quitar selección</button>
          </div>
        </div>
      </div>
    </Transition>

    <EventChart 
      :event-types="ALL_CHART_TYPES"
      :event-styles="EVENT_STYLES"
      :global-event-counts="combinedGlobalCounts"
      :player-event-counts="combinedPlayerCounts"
      :selected-player-uuid="selectedUuid"
      :player-bar-gradient="playerBarGradient"
      view-mode="ratio"
      :loading="loading"
      :filters-collapsed="filtersCollapsed"
      :active-filters="activeFilters"
      :group-totals="{
        offers: offersTotal,
        responses: responsesTotal,
        force: forceTotal,
        shame: shameTotal,
        report: reportTotal,
        averageScore: averageScoreTotal,
        totalPlayers: totalPlayersCount
      }"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import { useRouter } from 'vue-router';
import EventChart from '../components/EventChart.vue';
import EventFilters from '../components/EventFilters.vue';
import DataSourceSelector from '../components/DataSourceSelector.vue';
import { useEventFilters } from '../composables/useEventFilters';

interface RoomInfo { roomId: string; metadata?: any; }
interface RoomState { players?: any[]; systemMessages?: { kind: string }[] }

const router = useRouter();
const loading = ref(false);
const eventFilters = useEventFilters();
const filtersCollapsed = ref(false);


const EVENTS = [
  'p1_propose', 'p1_no_offer',
  'p2_snatch', 'p2_accept', 'p2_force', 'p2_no_force', 'p2_reject',
  'p1_shame', 'p1_no_shame', 'p1_report', 'p1_no_report'
];

// New metric types for additional charts
const METRICS = [
  'players_seated', 'score_p1', 'score_p2', 'players_with_shame', 'players_without_shame'
];

const ALL_CHART_TYPES = [...EVENTS, ...METRICS];

// Event and metric styles matching OfferActions and OfferControls components
const EVENT_STYLES: Record<string, { icon: string; color: string; gradient: string }> = {
  // Event types
  'p1_propose': { icon: '✨', color: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
  'p1_no_offer': { icon: '❌', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' },
  'p2_accept': { icon: '✓', color: '#10b981', gradient: 'linear-gradient(135deg, #10b981 0%, #059669 100%)' },
  'p2_reject': { icon: '✕', color: '#f59e0b', gradient: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)' },
  'p2_snatch': { icon: '👹', color: '#ef4444', gradient: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)' },
  'p2_force': { icon: '⚡', color: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
  'p2_no_force': { icon: '🚫', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' },
  'p1_shame': { icon: '😶', color: '#fbbf24', gradient: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)' },
  'p1_no_shame': { icon: '🙂', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' },
  'p1_report': { icon: '⚖️', color: '#8b5cf6', gradient: 'linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)' },
  'p1_no_report': { icon: '🤝', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' },
  
  // Metric types
  'players_seated': { icon: '👥', color: '#06b6d4', gradient: 'linear-gradient(135deg, #06b6d4 0%, #0891b2 100%)' },
  'score_p1': { icon: '🦃', color: '#16a34a', gradient: 'linear-gradient(135deg, #16a34a 0%, #15803d 100%)' },
  'score_p2': { icon: '🌽', color: '#d97706', gradient: 'linear-gradient(135deg, #d97706 0%, #b45309 100%)' },
  'players_with_shame': { icon: '😶', color: '#dc2626', gradient: 'linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)' },
  'players_without_shame': { icon: '👥', color: '#06b6d4', gradient: 'linear-gradient(135deg, #06b6d4 0%, #0891b2 100%)' }
};


// Additional metrics computation
const additionalMetrics = ref<Record<string, number>>({
  players_seated: 0,
  score_p1: 0,
  score_p2: 0,
  players_with_shame: 0,
  players_without_shame: 0
});

const selectedPlayerMetrics = ref<Record<string, number>>({
  players_seated: 0,
  score_p1: 0,
  score_p2: 0,
  players_with_shame: 0,
  players_without_shame: 0
});

// Store room score history from players
const allPlayersWithScores = ref<any[]>([]);

// Function to check if a score passes the current filters
function scorePassesFilters(score: any, roomId: string) {
  // Room filter
  if (eventFilters.roomFilter.value !== 'all' && roomId !== eventFilters.roomFilter.value) {
    return false;
  }
  
  // Round filter
  if (eventFilters.roundFilter.value !== 'all' && score.round !== eventFilters.roundFilter.value) {
    return false;
  }
  
  // Game variant filter
  if (eventFilters.gameFilter.value !== 'all' && score.variant !== eventFilters.gameFilter.value) {
    return false;
  }
  
  return true;
}

// Function to compute additional metrics from players' score history
function computeMetricsFromScores() {
  if (!allPlayersWithScores.value.length) return;
  
  let totalP1Scores = 0;
  let totalP2Scores = 0;
  let p1Count = 0;
  let p2Count = 0;
  let playersWithShame = 0;
  let totalPlayersWithNames = 0;

  // Get score data from players with room score history
  allPlayersWithScores.value.forEach((player: any) => {
    if (player.name) {
      totalPlayersWithNames++;
      
      // Count players with shame tokens
      if (player.shameTokens && player.shameTokens > 0) {
        playersWithShame++;
      }
      
      // Extract scores from roomScoreHistory if available
      if (player.roomScoreHistory) {
        player.roomScoreHistory.forEach((roomScore: any) => {
          roomScore.scores.forEach((score: any) => {
            // Apply filters to scores
            if (!scorePassesFilters(score, roomScore.roomId)) {
              return;
            }
            
            if (score.role === 'P1') {
              totalP1Scores += score.score;
              p1Count++;
            } else if (score.role === 'P2') {
              totalP2Scores += score.score;
              p2Count++;
            }
          });
        });
      }
    }
  });

  const avgP1Score = p1Count > 0 ? totalP1Scores / p1Count : 0;
  const avgP2Score = p2Count > 0 ? totalP2Scores / p2Count : 0;
  
  additionalMetrics.value = {
    players_seated: totalPlayersWithNames,
    score_p1: Math.round(avgP1Score * 10) / 10, // Round to 1 decimal
    score_p2: Math.round(avgP2Score * 10) / 10,
    players_with_shame: playersWithShame,
    players_without_shame: totalPlayersWithNames - playersWithShame
  };
}

// Function to compute metrics for a selected player
function computeSelectedPlayerMetrics(uuid: string) {
  const playerData = allPlayersWithScores.value.find(p => p.uuid === uuid);
  if (!playerData) {
    selectedPlayerMetrics.value = {
      players_seated: 1,
      score_p1: 0,
      score_p2: 0,
      players_with_shame: 0,
      players_without_shame: 1
    };
    return;
  }

  let totalP1Scores = 0;
  let totalP2Scores = 0;
  let p1Count = 0;
  let p2Count = 0;
  
  const hasShame = playerData.shameTokens && playerData.shameTokens > 0;

  if (playerData.roomScoreHistory) {
    playerData.roomScoreHistory.forEach((roomScore: any) => {
      roomScore.scores.forEach((score: any) => {
        // Apply filters to scores
        if (!scorePassesFilters(score, roomScore.roomId)) {
          return;
        }
        
        if (score.role === 'P1') {
          totalP1Scores += score.score;
          p1Count++;
        } else if (score.role === 'P2') {
          totalP2Scores += score.score;
          p2Count++;
        }
      });
    });
  }

  selectedPlayerMetrics.value = {
    players_seated: 1,
    score_p1: p1Count > 0 ? Math.round((totalP1Scores / p1Count) * 10) / 10 : 0,
    score_p2: p2Count > 0 ? Math.round((totalP2Scores / p2Count) * 10) / 10 : 0,
    players_with_shame: hasShame ? 1 : 0,
    players_without_shame: hasShame ? 0 : 1
  };
}

// Combined counts for charts (events + metrics)
const combinedGlobalCounts = computed(() => ({
  ...eventFilters.globalEventCounts.value,
  ...additionalMetrics.value
}));

const combinedPlayerCounts = computed(() => ({
  ...playerEventCounts.value,
  ...selectedPlayerMetrics.value
}));


// Chart group totals
const offersTotal = computed(() => {
  const propose = eventFilters.globalEventCounts.value.p1_propose || 0;
  const noOffer = eventFilters.globalEventCounts.value.p1_no_offer || 0;
  return propose + noOffer;
});

const responsesTotal = computed(() => {
  const accept = eventFilters.globalEventCounts.value.p2_accept || 0;
  const reject = eventFilters.globalEventCounts.value.p2_reject || 0;
  const snatch = eventFilters.globalEventCounts.value.p2_snatch || 0;
  return accept + reject + snatch;
});

const forceTotal = computed(() => {
  const force = eventFilters.globalEventCounts.value.p2_force || 0;
  const noForce = eventFilters.globalEventCounts.value.p2_no_force || 0;
  return force + noForce;
});

const shameTotal = computed(() => {
  const shame = eventFilters.globalEventCounts.value.p1_shame || 0;
  const noShame = eventFilters.globalEventCounts.value.p1_no_shame || 0;
  return shame + noShame;
});

const reportTotal = computed(() => {
  const report = eventFilters.globalEventCounts.value.p1_report || 0;
  const noReport = eventFilters.globalEventCounts.value.p1_no_report || 0;
  return report + noReport;
});

const averageScoreTotal = computed(() => {
  if (!allPlayersWithScores.value.length) return 0;
  
  let totalScores = 0;
  let totalScoreCount = 0;

  // Sum all individual scores from all players regardless of role, applying filters
  allPlayersWithScores.value.forEach((player: any) => {
    if (player.roomScoreHistory) {
      player.roomScoreHistory.forEach((roomScore: any) => {
        roomScore.scores.forEach((score: any) => {
          // Apply filters to scores
          if (!scorePassesFilters(score, roomScore.roomId)) {
            return;
          }
          
          totalScores += score.score;
          totalScoreCount++;
        });
      });
    }
  });

  return totalScoreCount > 0 ? Math.round((totalScores / totalScoreCount) * 10) / 10 : 0;
});

const totalPlayersCount = computed(() => {
  return additionalMetrics.value.players_seated || 0;
});

// Active filters object
const activeFilters = computed(() => ({
  dataSource: eventFilters.dataSource.value,
  round: eventFilters.roundFilter.value.toString(),
  game: eventFilters.gameFilter.value,
  hasFilters: eventFilters.hasActiveFilters.value || !!selectedRoomId.value,
  selectedPlayer: selectedUuid.value ? players.value.find(p => p.uuid === selectedUuid.value)?.name || 'Jugador' : undefined,
  selectedRoom: selectedRoomId.value ? availableRooms.value.find(r => r.roomId === selectedRoomId.value)?.name || 'Sala' : undefined
}));

// Watch for changes in filters and data source
watch([eventFilters.dataSource, eventFilters.roundFilter, eventFilters.gameFilter, eventFilters.roomFilter], () => {
  eventFilters.applyFilters(EVENTS);
  // Recalculate metrics when filters change
  computeMetricsFromScores();
});

// selectedUuid watch will be added after selectedUuid is declared

const rooms = ref<RoomInfo[]>([]);
const players = ref<{ uuid: string; name: string; color?: string }[]>([]);
const availableRooms = ref<{ roomId: string; name: string; playerCount?: number }[]>([]);
const search = ref('');
const roomSearch = ref('');
const playersFiltered = computed(() => {
  const q = (search.value || '').toLowerCase();
  if (!q) return players.value;
  return players.value.filter(p => (p.name || '').toLowerCase().includes(q) || (p.uuid || '').toLowerCase().includes(q));
});

const roomsFiltered = computed(() => {
  const q = (roomSearch.value || '').toLowerCase();
  if (!q) return availableRooms.value;
  return availableRooms.value.filter(r => 
    (r.roomId || '').toLowerCase().includes(q) || 
    (r.name || '').toLowerCase().includes(q)
  );
});

// Reset page when search changes
watch(search, () => {
  page.value = 1;
});
watch(roomSearch, () => {
  roomPage.value = 1;
});
const page = ref(1);
const roomPage = ref(1);
const containerWidth = ref(1200); // Default width, will be updated dynamically
const dynamicPageSize = computed(() => {
  // Estimate space needed per chip and controls
  const chipWidth = 140; // Average chip width including gap
  const clearBtnWidth = selectedUuid.value ? 150 : 0; // "Quitar selección" button
  const searchWidth = 240; // Search input
  const paginationWidth = pageCount.value > 1 ? 100 : 0; // Compact pagination
  const margin = 40; // Container margins
  
  const availableWidth = containerWidth.value - searchWidth - paginationWidth - clearBtnWidth - margin;
  const maxChips = Math.max(3, Math.floor(availableWidth / chipWidth)); // Minimum 3 chips
  
  return Math.min(maxChips, 15); // Maximum 15 chips per page
});

const roomDynamicPageSize = computed(() => {
  // Similar calculation for rooms
  const chipWidth = 160; // Room chips might be slightly wider
  const clearBtnWidth = selectedRoomId.value ? 150 : 0; 
  const searchWidth = 240;
  const paginationWidth = roomPageCount.value > 1 ? 100 : 0;
  const margin = 40;
  
  const availableWidth = containerWidth.value - searchWidth - paginationWidth - clearBtnWidth - margin;
  const maxChips = Math.max(3, Math.floor(availableWidth / chipWidth));
  
  return Math.min(maxChips, 10); // Maximum 10 room chips per page
});

const pageCount = computed(() => Math.max(1, Math.ceil((playersFiltered.value.length || 0) / dynamicPageSize.value)));
const roomPageCount = computed(() => Math.max(1, Math.ceil((roomsFiltered.value.length || 0) / roomDynamicPageSize.value)));

const playersPage = computed(() => {
  const start = (page.value - 1) * dynamicPageSize.value;
  return playersFiltered.value.slice(start, start + dynamicPageSize.value);
});

const roomsPage = computed(() => {
  const start = (roomPage.value - 1) * roomDynamicPageSize.value;
  return roomsFiltered.value.slice(start, start + roomDynamicPageSize.value);
});

const selectedUuid = ref('');
const selectedRoomId = ref('');
const playerLoading = ref(false);
const playerEventCounts = ref<Record<string, number>>((Object.fromEntries(EVENTS.map(k => [k, 0])) as Record<string, number>));
const playersActionsByUuid = ref<Record<string, Record<string, number>>>({});

// Watch for selected player changes to update their metrics
watch(selectedUuid, (newUuid) => {
  if (newUuid) {
    computeSelectedPlayerMetrics(newUuid);
  }
});

function initials(name: string): string {
  const n = (name || '').trim();
  if (!n) return '🙂';
  const parts = n.split(/\s+/).slice(0, 2);
  const chars = parts.map(p => p[0]?.toUpperCase()).join('');
  return chars || '🙂';
}


function selectPlayer(uuid: string) {
  if (selectedUuid.value === uuid) return;
  selectedUuid.value = uuid;
  loadPlayerHistory();
}

function clearPlayer() {
  selectedUuid.value = '';
}

function selectRoom(roomId: string) {
  if (selectedRoomId.value === roomId) return;
  selectedRoomId.value = roomId;
  eventFilters.roomFilter.value = roomId;
  eventFilters.applyFilters(EVENTS);
}

function clearRoom() {
  selectedRoomId.value = '';
  eventFilters.roomFilter.value = 'all';
  eventFilters.applyFilters(EVENTS);
}

function goHome() {
  router.push('/');
}

function prevPage() { page.value = Math.max(1, page.value - 1); }
function nextPage() { page.value = Math.min(pageCount.value, page.value + 1); }
function prevRoomPage() { roomPage.value = Math.max(1, roomPage.value - 1); }
function nextRoomPage() { roomPage.value = Math.min(roomPageCount.value, roomPage.value + 1); }

// Ensure page doesn't exceed pageCount when players change
watch(pageCount, (newCount) => {
  if (page.value > newCount) {
    page.value = Math.max(1, newCount);
  }
});

watch(roomPageCount, (newCount) => {
  if (roomPage.value > newCount) {
    roomPage.value = Math.max(1, newCount);
  }
});

// Dynamic per-player overlay bar gradient and label color
const playerBarGradient = computed(() => {
  const p = players.value.find(x => x.uuid === selectedUuid.value);
  const c = p?.color || '#667eea';
  return `linear-gradient(90deg, ${c}, ${c})`;
});



const apiBase = (import.meta as any).env?.VITE_API_URL || `${window.location.protocol}//${window.location.host}/api`;
const esRooms = ref<EventSource|null>(null);
const esUuids = ref<EventSource|null>(null);
const esActions = ref<EventSource|null>(null);

function closeStreams() {
  try { esRooms.value?.close(); } catch {}
  try { esUuids.value?.close(); } catch {}
  try { esActions.value?.close(); } catch {}
  esRooms.value = null;
  esUuids.value = null;
  esActions.value = null;
}

function setupStreams() {
  loading.value = true;
  // Rooms stream
  closeStreams();
  esRooms.value = new EventSource(`${apiBase}/dashboard-stream`);
  esRooms.value.onmessage = (e) => {
    try {
      const data = JSON.parse((e as MessageEvent).data || '{}');
      const details = data?.roomDetails || {};
      
      // Room details are for active rooms, we'll get room data from player history instead
      
      // Collect detailed events for active rooms
      const detailedEvents: Array<{ kind: string; round?: number; gameVariant?: string; roomId?: string }> = [];
      const counts: Record<string, number> = Object.fromEntries(EVENTS.map(k => [k, 0])) as any;
      
      Object.entries(details).forEach(([roomId, d]: [string, any]) => {
        (Array.isArray(d?.systemMessages) ? d.systemMessages : []).forEach((m: any) => {
          const k = (m?.kind || '').toString();
          if (EVENTS.includes(k)) {
            counts[k] = (counts[k] || 0) + 1;
            detailedEvents.push({
              kind: k,
              round: m?.round,
              gameVariant: m?.gameVariant || m?.variant,
              roomId: m?.roomId || roomId // Use message roomId if available, otherwise use room key
            });
          }
        });
      });
      
      eventFilters.updateActiveRoomsData(detailedEvents, counts);
      
      // Compute additional metrics from active rooms (for current UI compatibility)
      // computeMetrics(details); // Removed since we now use score history
      
      // Don't extract rooms from active data - we'll get them from aggregated player history
      
      // Apply filters and update display
      if (eventFilters.dataSource.value === 'active-rooms') {
        eventFilters.applyFilters(EVENTS);
      }
      // Build players list from room details (keep color if provided)
      const playerMap = new Map<string, { name: string; color?: string }>();
      Object.values(details).forEach((d: any) => {
        (d?.players || []).forEach((p: any) => {
          const uuid = (p?.uuid || p?.sessionId || '').toString();
          if (uuid && !playerMap.has(uuid)) playerMap.set(uuid, { name: (p?.name || 'player'), color: p?.color });
        });
      });
      const merged = new Map<string, { name: string; color?: string }>();
      players.value.forEach(p => merged.set(p.uuid, { name: p.name, color: p.color }));
      playerMap.forEach((obj, uuid) => merged.set(uuid, { name: obj.name, color: obj.color || merged.get(uuid)?.color }));
      players.value = Array.from(merged.entries()).map(([uuid, obj]) => ({ uuid, name: obj.name, color: obj.color })).sort((a,b)=>a.name.localeCompare(b.name));
    } finally {
      loading.value = false;
    }
  };
  esRooms.value.onerror = () => {};

  // UUIDs stream
  esUuids.value = new EventSource(`${apiBase}/uuids-stream`);
  esUuids.value.onmessage = (e) => {
    try {
      const data = JSON.parse((e as MessageEvent).data || '{}');
      const list = Array.isArray(data?.uuids) ? data.uuids : [];
      const existing = new Map<string, { name: string; color?: string }>();
      players.value.forEach(p => existing.set(p.uuid, { name: p.name, color: p.color }));
      list.forEach((u: any) => {
        const uuid = (u?.uuid || '').toString();
        if (!uuid) return;
        const prev = existing.get(uuid);
        const name = (u?.name || prev?.name || 'player').toString();
        existing.set(uuid, { name, color: prev?.color });
      });
      players.value = Array.from(existing.entries()).map(([uuid, obj]) => ({ uuid, name: obj.name, color: obj.color })).sort((a,b)=>a.name.localeCompare(b.name));
    } catch {}
  };
  esUuids.value.onerror = () => {};

  // Per-player actions stream
  esActions.value = new EventSource(`${apiBase}/players-actions-stream`);
  esActions.value.onmessage = (e) => {
    try {
      const data = JSON.parse((e as MessageEvent).data || '{}');
      const list = Array.isArray(data?.players) ? data.players : [];
      allPlayersActions.value = list.map((p: any) => ({ uuid: String(p.uuid||''), name: String(p.name||''), total: Number(p.total||0) }));
      
      // Store complete player data with room score history
      allPlayersWithScores.value = list;
      
      
      // Collect all detailed events from all players
      const allDetailedEvents: Array<{ kind: string; round?: number; gameVariant?: string; roomId?: string }> = [];
      const aggregatedCounts: Record<string, number> = Object.fromEntries(EVENTS.map(k => [k, 0])) as any;
      
      // Update detailed counts map
      const byUuid: Record<string, Record<string, number>> = {};
      list.forEach((p: any) => {
        const uuid = String(p?.uuid || '');
        if (!uuid) return;
        const src = p?.counts || {};
        const normalized: Record<string, number> = Object.fromEntries(EVENTS.map(k => [k, 0])) as any;
        
        // Add detailed history events
        if (Array.isArray(p?.detailedHistory)) {
          allDetailedEvents.push(...p.detailedHistory);
        }
        
        EVENTS.forEach(k => { 
          const count = Number(src[k] || 0);
          normalized[k] = count;
          aggregatedCounts[k] = (aggregatedCounts[k] || 0) + count;
        });
        byUuid[uuid] = normalized;
      });
      
      playersActionsByUuid.value = byUuid;
      eventFilters.updateAggregatedData(allDetailedEvents, aggregatedCounts);
      
      // Extract unique room IDs from aggregated events - this is our primary source for rooms
      const roomIds = new Set<string>();
      allDetailedEvents.forEach(event => {
        if (event.roomId && event.roomId.trim()) {
          roomIds.add(event.roomId);
        }
      });
      
      // Build available rooms list from aggregated events
      availableRooms.value = Array.from(roomIds).map(roomId => ({
        roomId,
        name: `Sala ${roomId.slice(0, 8)}`,
        playerCount: allDetailedEvents.filter(e => e.roomId === roomId).length
      }));
      
      // Compute metrics from score history
      computeMetricsFromScores();
      
      // Apply filters and update display if viewing aggregated data
      if (eventFilters.dataSource.value === 'aggregated') {
        eventFilters.applyFilters(EVENTS);
      }
      // If a player is selected, update playerEventCounts live
      if (selectedUuid.value) {
        const counts = byUuid[selectedUuid.value];
        if (counts) playerEventCounts.value = counts as any;
      }
      // Merge names into players list; preserve colors from previous streams
      const existing = new Map<string, { name: string; color?: string }>();
      players.value.forEach(p => existing.set(p.uuid, { name: p.name, color: p.color }));
      list.forEach((u: any) => {
        const uuid = (u?.uuid || '').toString();
        if (!uuid) return;
        const prev = existing.get(uuid);
        const name = (u?.name || prev?.name || 'player').toString();
        existing.set(uuid, { name, color: prev?.color });
      });
      players.value = Array.from(existing.entries()).map(([uuid, obj]) => ({ uuid, name: obj.name, color: obj.color })).sort((a,b)=>a.name.localeCompare(b.name));
    } catch {}
  };
  esActions.value.onerror = () => {};
}

async function fetchRooms(): Promise<RoomInfo[]> {
  const res = await fetch(`${apiBase}/rooms`);
  return await res.json();
}

async function fetchRoomStats(roomId: string): Promise<RoomState> {
  const res = await fetch(`${apiBase}/rooms/${roomId}/stats`);
  return await res.json();
}

async function refreshAll() {
  loading.value = true;
  try {
    const list = await fetchRooms();
    rooms.value = (list || []).filter((r: any) => r?.name === 'game');
    // reset counts
    eventFilters.globalEventCounts.value = Object.fromEntries(EVENTS.map(k => [k, 0])) as Record<string, number>;
    const playerMap = new Map<string, { name: string; color?: string }>();

    for (const r of rooms.value) {
      try {
        const s = await fetchRoomStats(r.roomId);
        const msgs = (s?.systemMessages || []) as Array<{ kind: string }>;
        msgs.forEach(m => {
          const k = (m?.kind || '').toString();
          if (EVENTS.includes(k)) {
            eventFilters.globalEventCounts.value[k] = (eventFilters.globalEventCounts.value[k] || 0) + 1;
          }
        });
        // collect players for filter
        (s?.players || []).forEach((p: any) => {
          const uuid = (p?.uuid || p?.sessionId || '').toString();
          if (uuid && !playerMap.has(uuid)) playerMap.set(uuid, { name: (p?.name || 'player'), color: p?.color });
        });
      } catch {}
    }
    players.value = Array.from(playerMap.entries()).map(([uuid, obj]) => ({ uuid, name: obj.name, color: obj.color }));
  } finally {
    loading.value = false;
  }
}

async function loadPlayerHistory() {
  playerLoading.value = true;
  const next: Record<string, number> = Object.fromEntries(EVENTS.map(k => [k, 0])) as any;
  const counts = playersActionsByUuid.value[selectedUuid.value || ''];
  if (counts) {
    EVENTS.forEach(k => { next[k] = Number(counts[k] || 0); });
  }
  playerEventCounts.value = next as any;
  playerLoading.value = false;
}

// Update container width on resize
function updateContainerWidth() {
  containerWidth.value = window.innerWidth;
}

onMounted(() => {
  setupStreams();
  // Initialize with aggregated data as default
  eventFilters.globalEventCounts.value = { ...eventFilters.globalEventCountsAggregated.value };
  
  // Set initial container width and add resize listener
  updateContainerWidth();
  window.addEventListener('resize', updateContainerWidth);
});

onUnmounted(() => {
  closeStreams();
  window.removeEventListener('resize', updateContainerWidth);
});

// Reset to first page when search changes or players list length shrinks below current page
watch(() => search.value, () => { page.value = 1; });
watch(() => playersFiltered.value.length, () => {
  if (page.value > pageCount.value) page.value = pageCount.value;
});

// Removed totals table and sorting; keep actions stream for per-player counts only
const allPlayersActions = ref<{ uuid: string; name: string; total: number }[]>([]);


function downloadJSON() {
  // Create a comprehensive snapshot of current interface data
  const currentData = {
    metadata: {
      timestamp: new Date().toISOString(),
      dataSource: eventFilters.dataSource.value,
      filters: {
        room: eventFilters.roomFilter.value,
        round: eventFilters.roundFilter.value,
        game: eventFilters.gameFilter.value,
        hasActiveFilters: eventFilters.hasActiveFilters.value,
        filterSummary: eventFilters.filterSummary.value,
        selectedPlayer: selectedUuid.value ? {
          uuid: selectedUuid.value,
          name: players.value.find(p => p.uuid === selectedUuid.value)?.name || 'Jugador'
        } : null,
        selectedRoom: selectedRoomId.value ? {
          roomId: selectedRoomId.value,
          name: availableRooms.value.find(r => r.roomId === selectedRoomId.value)?.name || 'Sala'
        } : null
      }
    },
    
    // Current chart totals being displayed
    chartTotals: {
      offers: offersTotal.value,
      responses: responsesTotal.value,
      force: forceTotal.value,
      shame: shameTotal.value,
      report: reportTotal.value,
      averageScore: averageScoreTotal.value,
      totalPlayers: totalPlayersCount.value
    },
    
    // Global event counts (filtered)
    globalEventCounts: eventFilters.globalEventCounts.value,
    
    // Selected player event counts (if applicable)
    selectedPlayerEventCounts: selectedUuid.value ? playerEventCounts.value : null,
    
    // Additional metrics being displayed
    additionalMetrics: additionalMetrics.value,
    
    // Selected player metrics (if applicable) 
    selectedPlayerMetrics: selectedUuid.value ? selectedPlayerMetrics.value : null,
    
    // Combined counts used in charts
    combinedGlobalCounts: combinedGlobalCounts.value,
    combinedPlayerCounts: selectedUuid.value ? combinedPlayerCounts.value : null,
    
    // Players data
    players: players.value,
    availableRooms: availableRooms.value,
    
    // Current filtered data if available
    currentSourceEvents: eventFilters.currentSourceEvents.value,
    
    // Per-player action counts
    playersActionsByUuid: playersActionsByUuid.value,
    
    // All players with scores data
    allPlayersWithScores: allPlayersWithScores.value.map(player => ({
      uuid: player.uuid,
      name: player.name,
      total: player.total,
      shameTokens: player.shameTokens,
      counts: player.counts,
      roomScoreHistory: player.roomScoreHistory
    }))
  };
  
  // Convert to formatted JSON string
  const jsonString = JSON.stringify(currentData, null, 2);
  
  // Create and download file
  const blob = new Blob([jsonString], { type: 'application/json;charset=utf-8;' });
  const link = document.createElement('a');
  const url = URL.createObjectURL(blob);
  link.setAttribute('href', url);
  
  // Generate filename with current filters and timestamp
  const timestamp = new Date().toISOString().slice(0, 19).replace(/[T:]/g, '-');
  const dataSourceLabel = eventFilters.dataSource.value === 'aggregated' ? 'agregados' : 'activos';
  const filterLabel = eventFilters.hasActiveFilters.value ? `_${eventFilters.filterSummary.value.replace(/\s+/g, '_')}` : '';
  const filename = `leaderboard_${dataSourceLabel}${filterLabel}_${timestamp}.json`;
  
  link.setAttribute('download', filename);
  link.style.visibility = 'hidden';
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url);
}

</script>

<style scoped>
/* Light theme aligned with other pages (UUID selector, lobby, game) */
.leaderboard.light { min-height: 100vh; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color:#0f172a; display:flex; flex-direction:column; }

.glass.light { background: rgba(255, 255, 255, 0.92); border: 1px solid rgba(229, 231, 235, 0.95); box-shadow: 0 18px 50px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.75); backdrop-filter: blur(18px) saturate(120%); -webkit-backdrop-filter: blur(18px) saturate(120%); border-radius: 16px; }

.header { display:flex; align-items:center; justify-content:space-between; gap: 8px; flex-wrap: wrap; padding: 8px 10px; margin-bottom: 10px; }
.header h1 { margin: 0; font-size: 18px; line-height: 1.2; flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.header-left { display:flex; align-items:center; gap: 10px; flex: 1 1 auto; min-width: 200px; }
.btn-back { background:#667eea; color:#fff; border:none; border-radius:6px; padding:6px 10px; font-weight:600; cursor:pointer; transition: all 0.3s ease; font-size: 12px; }
.btn-back:hover { background:#5b6bda; transform: translateY(-1px); box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); }
.actions { display:flex; gap: 6px; flex-wrap: wrap; align-items: center; justify-content: flex-end; flex: 1 1 280px; }
.actions .btn { background:#667eea; color:#fff; border:none; border-radius:6px; padding:4px 8px; font-weight:800; font-size: 11px; cursor:pointer; }
.actions .btn.toggle { background:#eef2ff; color:#3949ab; border:1px solid #c7d2fe; }
.actions .btn.toggle.active { background:#3949ab; color:#fff; border-color:#2e3f9a; }

.controls { display:grid; grid-template-columns: 1fr; gap: 10px; padding: 10px 12px; margin-bottom: 14px; }
.legend { font-size: 13px; color:#334155; display:flex; align-items:center; gap:10px; }
.key { width: 12px; height: 12px; border-radius: 999px; display:inline-block; }
.key.global { background: linear-gradient(90deg, #34d399, #10b981); box-shadow: 0 0 8px rgba(16,185,129,0.35); }
.key.player { background: linear-gradient(90deg, #a78bfa, #6366f1); box-shadow: 0 0 8px rgba(99,102,241,0.35); }
.key.room { background: linear-gradient(90deg, #f59e0b, #d97706); box-shadow: 0 0 8px rgba(245,158,11,0.35); }
.sep { opacity: 0.6; }

.player-chips, .room-chips { 
  display: flex; 
  flex-direction: column;
  gap: 12px;
}

.room-chips {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid rgba(203, 213, 225, 0.5);
}

.search-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: space-between;
}

.search { 
  padding: 8px 10px; 
  border: 1px solid #cbd5e1; 
  background: #fff; 
  color: #0f172a; 
  border-radius: 10px; 
  min-width: 240px; 
  outline: none; 
  flex: 1;
  max-width: 300px;
}

.search::placeholder { 
  color: #64748b; 
}

.chips { 
  display: flex; 
  gap: 10px; 
  flex-wrap: wrap; 
  align-items: center;
}
.chip { display:flex; align-items:center; gap:8px; background: color-mix(in srgb, var(--primary) 6%, white); border:1px solid color-mix(in srgb, var(--primary) 24%, #e5e7eb); padding:8px 12px; border-radius: 999px; color:#111827; cursor:pointer; transition: transform .18s ease, background .18s ease, box-shadow .18s ease; }
.chip:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--primary) 10%, white); box-shadow: 0 6px 18px rgba(102,126,234,0.18); }
.chip.active { background: color-mix(in srgb, var(--primary) 18%, white); border-color: color-mix(in srgb, var(--primary) 45%, #c7d2fe); box-shadow: 0 6px 22px rgba(99,102,241,0.22); }
.chip.clear { background:#fff; border-style:dashed; color:#334155; }
.chip.room-chip { --primary: #f59e0b; }
.chip.room-chip .count { 
  background: color-mix(in srgb, var(--primary) 20%, white); 
  color: color-mix(in srgb, var(--primary) 80%, #111); 
  font-size: 11px; 
  font-weight: 800; 
  padding: 2px 6px; 
  border-radius: 10px; 
  margin-left: 4px; 
}
.avatar { width: 24px; height: 24px; border-radius: 50%; background: color-mix(in srgb, var(--primary) 25%, #eef2ff); display:grid; place-items:center; font-weight:900; color: color-mix(in srgb, var(--primary) 80%, #111); }



/* Pagination styles */
.pagination {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: rgba(255,255,255,0.82);
  border: 1px solid rgba(229,231,235,0.9);
  border-radius: 999px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.6);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
}

.pagination.compact {
  gap: 6px;
  padding: 4px 8px;
}

.pg-btn {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid #cbd5e1;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  font-weight: 900;
  font-size: 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
  box-shadow: 0 4px 12px rgba(102,126,234,0.25);
}

.pg-btn.compact {
  width: 24px;
  height: 24px;
  font-size: 14px;
  font-weight: 800;
  box-shadow: 0 2px 8px rgba(102,126,234,0.2);
}

.pg-btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(102,126,234,0.35);
}

.pg-btn.compact:hover:not(:disabled) {
  box-shadow: 0 4px 12px rgba(102,126,234,0.3);
}

.pg-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: #e5e7eb;
  color: #94a3b8;
  box-shadow: none;
}

.pg-ind {
  font-weight: 600;
  color: #334155;
  font-size: 12px;
  min-width: 35px;
  text-align: center;
}


/* Filters section styles */
.filters-section {
  transition: all 0.3s ease;
}

.btn-collapse { 
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border: none;
  border-radius: 6px;
  background: #eef2ff;
  color: #3949ab;
  border: 1px solid #c7d2fe;
  font-weight: 800;
  font-size: 11px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.btn-collapse:hover {
  background: rgba(255,255,255,0.9);
  border-color: #cbd5e1;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}

.collapse-icon { transition: transform 0.3s ease; font-size: 11px; }

.collapse-icon.rotated {
  transform: rotate(-90deg);
}

.filters-content {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* Transition for filters */
.filters-slide-enter-active,
.filters-slide-leave-active {
  transition: all 0.3s ease;
  overflow: hidden;
}

.filters-slide-enter-from,
.filters-slide-leave-to {
  opacity: 0;
  max-height: 0;
  transform: translateY(-20px);
}

.filters-slide-enter-to,
.filters-slide-leave-from {
  opacity: 1;
  max-height: 200px;
  transform: translateY(0);
}

@media (max-width: 768px) {
  .header { padding: 6px 8px; margin-bottom: 8px; }
  .header h1 { font-size: 16px; }
  .btn-back { padding: 5px 8px; font-size: 11px; border-radius: 6px; }
  .actions { gap: 6px; }
  .actions .btn { padding: 4px 6px; font-size: 10px; border-radius: 5px; }
  .search-controls {
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
  }
  
  .search {
    min-width: auto;
    max-width: none;
  }
  
  .pagination.compact {
    align-self: center;
  }
}

@media (max-width: 640px) {
  .header { gap: 6px; }
  .header-left { min-width: 140px; }
  .actions { justify-content: flex-start; }
  .btn-collapse .collapse-text {
    display: none;
  }
  
  .btn-collapse {
    padding: 4px 6px;
    min-width: 34px;
    justify-content: center;
    font-size: 10px;
    border-radius: 5px;
  }
}

@media (max-width: 480px) {
  .header h1 { font-size: 14px; }
  .btn-back { padding: 4px 6px; font-size: 10px; }
  .actions { gap: 4px; }
  .actions .btn { padding: 3px 5px; font-size: 9px; border-radius: 4px; }
  .btn-collapse { padding: 3px 5px; min-width: 30px; font-size: 9px; border-radius: 4px; }
  /* Ultra-compact: single-line header on very small screens */
  .header { flex-wrap: nowrap; padding: 4px 6px; }
  .header-left { flex: 0 1 auto; }
  .btn-back .label { display: none; }
  .actions .btn .label { display: none; }
  .header h1 { font-size: 13px; }
  .header h1 .emoji { display: none; }
  .player-chips {
    gap: 8px;
  }
  
  .chips {
    gap: 8px;
    justify-content: center;
  }
  
  .chip {
    padding: 6px 10px;
  }
  
  .pg-btn.compact {
    width: 20px;
    height: 20px;
    font-size: 12px;
  }
  
  .pg-ind {
    font-size: 11px;
    min-width: 30px;
  }
}

</style>
