import * as THREE from "three";
import {
  describeCompactStatsLine,
  describeNetworkPanel,
  describePerformancePanel,
  recordPerformanceStats,
  summarizeNetworkStats,
  summarizePerformanceStats,
} from "./viewerTelemetry";
import { updatePlanetPresentation } from "./viewerPresentation";
import { buildSystemPanelState } from "./viewerPanels";
import { describeGameStatus, renderRecentEvents, updateSystemSummaries, updateWorldPresentation } from "./viewerWorldPresentation";
import { createBackdropStars, createNebulaClouds, createNebulaTexture } from "./viewerSceneFactory";
import type { ViewerHudState } from "./viewerHudState";
import type { Selectable } from "./viewerTypes";

export interface ViewerPresentationContext {
  renderer: THREE.WebGLRenderer;
  hudState: ViewerHudState;
  galaxyScene: THREE.Scene;
  galaxyCamera: THREE.PerspectiveCamera;
  systemCamera: THREE.PerspectiveCamera;
  galaxyAnchor: THREE.Vector3;
  systemAnchor: THREE.Vector3;
  ambienceGroup: THREE.Group;
  networkStats: any;
  performanceStats: any;
  getWorld: () => any;
  getActiveSystemId: () => string | undefined;
  getCameraMode: () => any;
  getCameraTargetShipId: () => string | undefined;
  getPovLevel: () => any;
  getSelectedItems: () => Selectable[];
  getWorldTimeSyncMs: () => number;
  getCurrentDistance: () => number;
  planetVisuals: any[];
  systemVisuals: Map<any, any>;
  createWorldPresentationContext: () => any;
}

export class ViewerPresentationController {
  constructor(private readonly context: ViewerPresentationContext) { }

  private refreshStatsOverlayLines(gameBodyText?: string) {
    const mode = this.context.hudState.statsOverlay.mode;
    if (mode === "hidden") {
      this.context.hudState.statsOverlay.lines = [];
      return;
    }

    const compactLine = describeCompactStatsLine(this.context.networkStats, this.context.performanceStats);
    const gameLines = (gameBodyText ?? this.context.hudState.gamePanel.bodyText)
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean);
    const networkLines = this.context.hudState.networkPanel.bodyText
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean);
    const performanceLines = this.context.hudState.performancePanel.bodyText
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean);

    switch (mode) {
      case "compact":
        this.context.hudState.statsOverlay.lines = [compactLine];
        return;
      case "status":
        this.context.hudState.statsOverlay.lines = [compactLine, ...gameLines];
        return;
      case "network":
        this.context.hudState.statsOverlay.lines = [compactLine, ...networkLines];
        return;
      case "performance":
        this.context.hudState.statsOverlay.lines = [compactLine, ...performanceLines];
        return;
      case "full":
        this.context.hudState.statsOverlay.lines = [
          compactLine,
          "",
          ...gameLines,
          "",
          ...networkLines,
          "",
          ...performanceLines,
        ];
        return;
      default:
        this.context.hudState.statsOverlay.lines = [];
    }
  }

  initializeAmbience() {
    this.context.ambienceGroup.renderOrder = -10;
    this.context.ambienceGroup.add(createBackdropStars(document));
    this.context.ambienceGroup.add(...createNebulaClouds(createNebulaTexture(document)));
  }

  updateAmbience(_delta: number) {
    const activeCamera = this.context.getPovLevel() === "galaxy"
      ? this.context.galaxyCamera
      : this.context.systemCamera;
    this.context.ambienceGroup.position.copy(activeCamera.position);
  }

  applyZoomPresentation() {
    const povLevel = this.context.getPovLevel();

    this.context.galaxyScene.fog = new THREE.FogExp2(0x040912, 0.000035);

    const showPlanetIcons = povLevel !== "local";
    for (const visual of this.context.planetVisuals) {
      visual.icon.setVisible(showPlanetIcons);
    }
  }

  updateNetworkPanel() {
    this.context.hudState.networkPanel.bodyText = describeNetworkPanel(this.context.networkStats);
    this.context.hudState.networkPanel.summary = summarizeNetworkStats(this.context.networkStats);
    this.refreshStatsOverlayLines();
  }

  recordPerformanceStats(frameMs: number) {
    recordPerformanceStats(this.context.performanceStats, frameMs);
  }

  updatePerformancePanel() {
    const bodyText = describePerformancePanel(this.context.performanceStats, this.context.renderer);
    if (bodyText) {
      this.context.hudState.performancePanel.bodyText = bodyText;
    }
    this.context.hudState.performancePanel.summary = summarizePerformanceStats(this.context.performanceStats);
    this.refreshStatsOverlayLines();
  }

  updateShipPresentation() {
    updateWorldPresentation(this.context.createWorldPresentationContext());
  }

  updatePlanetPresentation() {
    const world = this.context.getWorld();
    updatePlanetPresentation(
      world,
      this.context.getWorldTimeSyncMs(),
      this.context.planetVisuals,
      this.context.systemCamera,
    );
  }

  updateSystemSummaries() {
    updateSystemSummaries(this.context.getWorld(), new Map());
  }

  renderRecentEvents(entityKind: string, entityId: string) {
    return renderRecentEvents(this.context.getWorld(), entityKind, entityId);
  }

  updateGamePanel(mode: string) {
    const state = describeGameStatus({
      world: this.context.getWorld(),
      activeSystemId: this.context.getActiveSystemId(),
      cameraMode: this.context.getCameraMode(),
      povLevel: this.context.getPovLevel(),
      selectedItems: this.context.getSelectedItems(),
      mode,
      galaxyAnchor: this.context.galaxyAnchor,
      systemAnchor: this.context.systemAnchor,
    });
    this.context.hudState.gamePanel.bodyText = state.bodyText;
    this.context.hudState.gamePanel.summary = state.summaryText;
    this.refreshStatsOverlayLines(state.bodyText);
  }

  refreshStatsOverlay() {
    this.refreshStatsOverlayLines();
  }

  updateSystemPanel() {
    const world = this.context.getWorld();
    if (!world) {
      return;
    }

    const state = buildSystemPanelState({
      world,
      activeSystemId: this.context.getActiveSystemId(),
      cameraMode: this.context.getCameraMode(),
      cameraTargetShipId: this.context.getCameraTargetShipId(),
    });
    this.context.hudState.systemPanel.hidden = state.hidden;
    this.context.hudState.systemPanel.title = state.title;
    this.context.hudState.systemPanel.bodyHtml = state.bodyHtml;
  }

  screenPointFromClient(clientX: number, clientY: number) {
    const bounds = this.context.renderer.domElement.getBoundingClientRect();
    return new THREE.Vector2(clientX - bounds.left, clientY - bounds.top);
  }

}
