import {
  DEFAULT_SCENE_ACCENT_COLOR,
  DEFAULT_SCENE_BACKGROUND_COLOR,
} from "@/lib/studio-color-palettes";
import type { Layer, Scene, SceneTransition } from "@/lib/studio-types";
import { DEFAULT_SCENE_DURATION } from "@/lib/studio-types";

const SCENE_TRANSITIONS: SceneTransition[] = [
  "none",
  "fade",
  "slide-left",
  "zoom",
];

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function parseLayer(value: unknown): Layer | null {
  if (!isRecord(value)) return null;
  if (typeof value.id !== "string" || typeof value.type !== "string") return null;
  if (
    typeof value.x !== "number" ||
    typeof value.y !== "number" ||
    typeof value.width !== "number" ||
    typeof value.height !== "number"
  ) {
    return null;
  }
  return {
    id: value.id,
    type: value.type as Layer["type"],
    name: typeof value.name === "string" ? value.name : undefined,
    visible: typeof value.visible === "boolean" ? value.visible : undefined,
    x: value.x,
    y: value.y,
    width: value.width,
    height: value.height,
    rotation: typeof value.rotation === "number" ? value.rotation : 0,
    opacity: typeof value.opacity === "number" ? value.opacity : 1,
    zIndex: typeof value.zIndex === "number" ? value.zIndex : 0,
    props: isRecord(value.props) ? value.props : {},
  };
}

/**
 * Bridge a V2 template/saved-project scene (content-element model, no canvas
 * geometry) into editable studio layers so every input shows in the editor.
 * Text/TextArea → text layer; Media/Image/Video/Audio → image layer. Laid out in a
 * column since AE templates carry no canvas coords. One-time on first load; after the
 * user edits + saves, the scene persists in the studio's own layer format.
 */
function layersFromContents(contents: unknown[]): Layer[] {
  const str = (v: unknown) => (typeof v === "string" ? v : "");
  const num = (v: unknown) => (typeof v === "number" ? v : undefined);
  const items = contents.filter(isRecord);
  items.sort(
    (a, b) =>
      (num(a.sort) ?? num(a.positionInContainer) ?? 0) -
      (num(b.sort) ?? num(b.positionInContainer) ?? 0)
  );
  return items.map((c, i) => {
    const type = str(c.type).toLowerCase();
    const isMedia = ["media", "image", "video", "audio", "voiceover"].includes(type);
    const key = str(c.key) || str(c.id) || `el${i}`;
    const value = str(c.value) || str(c.defaultValue) || str(c.default_value);
    return {
      id: `c-${key}`,
      type: isMedia ? "image" : "text",
      name: str(c.title) || key,
      x: 160,
      y: 160 + i * 150,
      width: 1600,
      height: isMedia ? 360 : 110,
      rotation: 0,
      opacity: 1,
      zIndex: i,
      props: isMedia
        ? { src: value }
        : {
            text: value || (str(c.title) || key),
            fontSize: 48,
            fill: "#111827",
            fontFamily: "Inter, sans-serif",
            align: "center",
          },
    } as Layer;
  });
}

function parseScene(value: unknown): Scene | null {
  if (!isRecord(value)) return null;
  // Studio's own save format uses string ids; the V2 relational assembly uses numeric
  // ids — accept both (coerce to string).
  const id =
    typeof value.id === "string"
      ? value.id
      : typeof value.id === "number"
        ? String(value.id)
        : null;
  if (id === null) return null;
  const name =
    typeof value.name === "string"
      ? value.name
      : typeof value.title === "string"
        ? value.title
        : typeof value.key === "string"
          ? value.key
          : "Scene";

  // Studio's own format carries `layers`; a freshly-copied V2 template carries
  // `contents` (content elements / inputs) instead — bridge those to layers.
  let layers: Layer[];
  if (Array.isArray(value.layers)) {
    layers = value.layers
      .map(parseLayer)
      .filter((layer): layer is Layer => layer !== null);
  } else if (Array.isArray(value.contents)) {
    layers = layersFromContents(value.contents);
  } else {
    return null;
  }

  const transitionType =
    typeof value.transitionType === "string" &&
    SCENE_TRANSITIONS.includes(value.transitionType as SceneTransition)
      ? (value.transitionType as SceneTransition)
      : "none";

  return {
    id,
    name,
    duration:
      typeof value.duration === "number"
        ? value.duration
        : typeof value.sceneLengthSec === "number"
          ? value.sceneLengthSec
          : typeof value.defaultDurationSec === "number"
            ? value.defaultDurationSec
            : DEFAULT_SCENE_DURATION,
    layers,
    transitionType,
  };
}

export function parseVideoScenes(value: unknown): Scene[] {
  if (!Array.isArray(value)) return [];
  return value.map(parseScene).filter((scene): scene is Scene => scene !== null);
}

export function isVideoSceneDataEmpty(sceneData: Record<string, unknown>): boolean {
  const scenes = parseVideoScenes(sceneData.scenes);
  return scenes.length === 0;
}

/** Omit generated thumbnails — they are re-created in the editor */
export function scenesForPersistence(scenes: Scene[]): Scene[] {
  return scenes.map((scene) => {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars -- strip before save
    const { thumbnailUrl, ...rest } = scene;
    return rest;
  });
}

export interface VideoPersistedSceneData {
  scenes: Scene[];
  activeSceneId: string;
  currentTime?: number;
  pxPerSecond?: number;
  audioFileName?: string | null;
  audioSrc?: string | null;
  audioVolume?: number;
  sceneBackgroundColor?: string;
  sceneAccentColor?: string;
  /** Project render mode (FIX / FLEXIBLE / MusicVisualizer / …). FIX/MusicVisualizer
   * scenes come from AE layer names, so adding scenes is disabled. */
  chooseMode?: string;
}

export function buildVideoSceneDataPayload(
  input: VideoPersistedSceneData
): Record<string, unknown> {
  return {
    scenes: scenesForPersistence(input.scenes),
    activeSceneId: input.activeSceneId,
    currentTime: input.currentTime,
    pxPerSecond: input.pxPerSecond,
    audioFileName: input.audioFileName,
    audioSrc: input.audioSrc,
    audioVolume: input.audioVolume,
    sceneBackgroundColor: input.sceneBackgroundColor,
    sceneAccentColor: input.sceneAccentColor,
  };
}

export function parseVideoSceneData(
  sceneData: Record<string, unknown>
): VideoPersistedSceneData | null {
  const scenes = parseVideoScenes(sceneData.scenes);
  if (scenes.length === 0) return null;

  const activeSceneId =
    typeof sceneData.activeSceneId === "string" &&
    scenes.some((scene) => scene.id === sceneData.activeSceneId)
      ? sceneData.activeSceneId
      : scenes[0].id;

  return {
    scenes,
    activeSceneId,
    currentTime:
      typeof sceneData.currentTime === "number" ? sceneData.currentTime : 0,
    pxPerSecond:
      typeof sceneData.pxPerSecond === "number" ? sceneData.pxPerSecond : undefined,
    audioFileName:
      typeof sceneData.audioFileName === "string" ? sceneData.audioFileName : null,
    audioSrc: typeof sceneData.audioSrc === "string" ? sceneData.audioSrc : null,
    audioVolume:
      typeof sceneData.audioVolume === "number" ? sceneData.audioVolume : 100,
    sceneBackgroundColor:
      typeof sceneData.sceneBackgroundColor === "string"
        ? sceneData.sceneBackgroundColor
        : DEFAULT_SCENE_BACKGROUND_COLOR,
    sceneAccentColor:
      typeof sceneData.sceneAccentColor === "string"
        ? sceneData.sceneAccentColor
        : DEFAULT_SCENE_ACCENT_COLOR,
    chooseMode:
      typeof sceneData.chooseMode === "string"
        ? sceneData.chooseMode
        : typeof sceneData.primaryMode === "string"
          ? sceneData.primaryMode
          : undefined,
  };
}
