import type {
  FactionSnapshot,
  WorldDelta,
  WorldSnapshot,
} from "./contracts";
import type {
  NetworkStats,
  PerformanceStats,
  WorldState,
} from "./viewerTypes";

export function createInitialNetworkStats(): NetworkStats {
  return {
    snapshotBytes: 0,
    deltasReceived: 0,
    deltaBytes: 0,
    lastDeltaBytes: 0,
    lastEntityChanges: 0,
    eventsReceived: 0,
    streamConnected: false,
    throughputSamples: [],
  };
}

export function createInitialPerformanceStats(): PerformanceStats {
  return {
    frameSamples: [],
    lastFrameMs: 0,
    lastPanelUpdateAtMs: 0,
  };
}

export function createWorldState(snapshot: WorldSnapshot): WorldState {
  return {
    label: snapshot.label,
    seed: snapshot.seed,
    sequence: snapshot.sequence,
    tickIntervalMs: snapshot.tickIntervalMs,
    orbitalTimeSeconds: snapshot.orbitalTimeSeconds,
    orbitalSimulation: snapshot.orbitalSimulation,
    generatedAtUtc: snapshot.generatedAtUtc,
    systems: new Map(snapshot.systems.map((system) => [system.id, system])),
    celestials: new Map(snapshot.celestials.map((celestial) => [celestial.id, celestial])),
    nodes: new Map(snapshot.nodes.map((node) => [node.id, node])),
    stations: new Map(snapshot.stations.map((station) => [station.id, station])),
    claims: new Map(snapshot.claims.map((claim) => [claim.id, claim])),
    constructionSites: new Map(snapshot.constructionSites.map((site) => [site.id, site])),
    marketOrders: new Map(snapshot.marketOrders.map((order) => [order.id, order])),
    policies: new Map(snapshot.policies.map((policy) => [policy.id, policy])),
    ships: new Map(snapshot.ships.map((ship) => [ship.id, ship])),
    factions: new Map(snapshot.factions.map((faction) => [faction.id, faction])),
    geopolitics: snapshot.geopolitics ?? null,
    recentEvents: [],
  };
}

export function applyDeltaToWorld(world: WorldState, delta: WorldDelta): boolean {
  world.sequence = delta.sequence;
  world.tickIntervalMs = delta.tickIntervalMs;
  world.orbitalTimeSeconds = delta.orbitalTimeSeconds;
  world.orbitalSimulation = delta.orbitalSimulation;
  world.generatedAtUtc = delta.generatedAtUtc;
  world.recentEvents = [...delta.events, ...world.recentEvents].slice(0, 18);

  for (const celestial of delta.celestials) {
    world.celestials.set(celestial.id, celestial);
  }
  for (const node of delta.nodes) {
    world.nodes.set(node.id, node);
  }
  for (const station of delta.stations) {
    world.stations.set(station.id, station);
  }
  for (const claim of delta.claims) {
    world.claims.set(claim.id, claim);
  }
  for (const site of delta.constructionSites) {
    world.constructionSites.set(site.id, site);
  }
  for (const order of delta.marketOrders) {
    world.marketOrders.set(order.id, order);
  }
  for (const policy of delta.policies) {
    world.policies.set(policy.id, policy);
  }
  for (const ship of delta.ships) {
    world.ships.set(ship.id, ship);
  }
  for (const faction of delta.factions) {
    world.factions.set(faction.id, faction);
  }
  if (delta.geopolitics !== undefined) {
    world.geopolitics = delta.geopolitics ?? null;
  }

  return delta.factions.length > 0 || delta.geopolitics !== undefined;
}

export function recordDeltaStats(networkStats: NetworkStats, delta: WorldDelta, rawBytes: number): void {
  const changedEntities = delta.ships.length
    + delta.stations.length
    + delta.nodes.length
    + delta.celestials.length
    + delta.claims.length
    + delta.constructionSites.length
    + delta.marketOrders.length
    + delta.policies.length
    + delta.factions.length;
  const changedEntitiesWithPlayer = changedEntities + (delta.geopolitics ? 1 : 0);
  networkStats.deltasReceived += 1;
  networkStats.deltaBytes += rawBytes;
  networkStats.lastDeltaBytes = rawBytes;
  networkStats.lastEntityChanges = changedEntitiesWithPlayer;
  networkStats.eventsReceived += delta.events.length;
  networkStats.lastDeltaAtMs = performance.now();
  networkStats.throughputSamples.push({ atMs: performance.now(), bytes: rawBytes });
  const cutoff = performance.now() - 4000;
  networkStats.throughputSamples = networkStats.throughputSamples.filter((sample) => sample.atMs >= cutoff);
}

export function cloneFactions(world: WorldState): FactionSnapshot[] {
  return [...world.factions.values()];
}
