/** Renderer-independent bridge from deterministic flight state to realtime protocol state. */

import type { AircraftPoseSnapshot } from "../realtime/types.ts";
import type { AircraftControllerSnapshot } from "./controller.ts";

export interface AircraftRealtimeIdentity {
  aircraftId: string;
  pilotActorId: string;
}

function identity(value: string, name: string): string {
  if (typeof value !== "string" || value.length === 0 || value.length > 256) {
    throw new RangeError(`${name} must be a non-empty string of at most 256 characters`);
  }
  return value;
}

/**
 * Converts one controller snapshot into the exact geographic wire contract.
 * Geographic velocity axes are east/up/north; heading zero therefore travels
 * north even though the Three.js presentation model faces local -Z.
 */
export function createAircraftPoseSnapshot(
  identityValue: AircraftRealtimeIdentity,
  state: AircraftControllerSnapshot,
  sequence: number,
  timestampMs: number,
): AircraftPoseSnapshot {
  const heading = state.headingDeg * Math.PI / 180;
  const pitch = state.pitchDeg * Math.PI / 180;
  const horizontalSpeedMps = state.speedMps * Math.cos(pitch);
  return {
    entity: "aircraft",
    aircraftId: identity(identityValue.aircraftId, "aircraftId"),
    kind: "electric-vtail",
    pilotActorId: identity(identityValue.pilotActorId, "pilotActorId"),
    sequence,
    timestampMs,
    pose: {
      space: "geographic",
      lat: state.lat,
      lng: state.lng,
      altitudeM: state.altitudeM,
      headingDeg: state.headingDeg,
      pitchDeg: state.pitchDeg,
    },
    velocity: {
      xMps: Math.sin(heading) * horizontalSpeedMps,
      yMps: state.verticalSpeedMps,
      zMps: Math.cos(heading) * horizontalSpeedMps,
      yawDegPerSec: 0,
    },
    rollDeg: state.rollDeg,
    throttle: state.throttle,
    rollInput: state.rollInput,
    pitchInput: state.pitchInput,
    yawInput: state.yawInput,
    fanRadians: state.fanRadians,
  };
}
