import { describe, expect, it } from "vitest";

import { createModelInstance } from "../../src/assets/model-instances";
import { createBoxBrush } from "../../src/document/brushes";
import { createScenePath } from "../../src/document/paths";
import { createEmptySceneDocument } from "../../src/document/scene-document";
import { createTerrain } from "../../src/document/terrains";
import {
  createCameraRigEntity,
  createPointLightEntity,
  createPlayerStartEntity,
  createSpotLightEntity,
  createTriggerVolumeEntity
} from "../../src/entities/entity-instances";
import { resolveViewportFocusTarget } from "../../src/viewport-three/viewport-focus";

describe("resolveViewportFocusTarget", () => {
  it("frames the selected brush", () => {
    const brush = createBoxBrush({
      id: "brush-room",
      center: {
        x: 3,
        y: 2,
        z: -1
      },
      size: {
        x: 6,
        y: 4,
        z: 2
      }
    });
    const document = {
      ...createEmptySceneDocument(),
      brushes: {
        [brush.id]: brush
      }
    };

    expect(
      resolveViewportFocusTarget(document, {
        kind: "brushes",
        ids: [brush.id]
      })
    ).toEqual({
      center: {
        x: 3,
        y: 2,
        z: -1
      },
      radius: Math.hypot(6, 4, 2) * 0.5
    });
  });

  it("frames rotated whitebox boxes around their authored center with a stable object radius", () => {
    const brush = createBoxBrush({
      id: "brush-rotated-room",
      center: {
        x: 1.25,
        y: 1.5,
        z: -0.75
      },
      rotationDegrees: {
        x: 0,
        y: 45,
        z: 0
      },
      size: {
        x: 2,
        y: 2,
        z: 4
      }
    });
    const document = {
      ...createEmptySceneDocument(),
      brushes: {
        [brush.id]: brush
      }
    };

    expect(
      resolveViewportFocusTarget(document, {
        kind: "brushes",
        ids: [brush.id]
      })
    ).toEqual({
      center: {
        x: 1.25,
        y: 1.5,
        z: -0.75
      },
      radius: Math.hypot(2, 2, 4) * 0.5
    });
  });

  it("frames the owning brush when a face is selected", () => {
    const brush = createBoxBrush({
      id: "brush-face-room"
    });
    const document = {
      ...createEmptySceneDocument(),
      brushes: {
        [brush.id]: brush
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "brushFace",
      brushId: brush.id,
      faceId: "posZ"
    });

    expect(focusTarget?.center).toEqual(brush.center);
    expect(focusTarget?.radius).toBe(Math.hypot(2, 2, 2) * 0.5);
  });

  it("frames the selected Player Start helper", () => {
    const playerStart = createPlayerStartEntity({
      id: "entity-player-start-main",
      position: {
        x: 4,
        y: 0,
        z: -2
      },
      yawDegrees: 90
    });
    const document = {
      ...createEmptySceneDocument(),
      entities: {
        [playerStart.id]: playerStart
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "entities",
      ids: [playerStart.id]
    });

    expect(focusTarget?.center).toEqual({
      x: 4,
      y: 0.3,
      z: -2
    });
    expect(focusTarget?.radius).toBeGreaterThan(0.6);
  });

  it("frames the selected Point Light helper", () => {
    const pointLight = createPointLightEntity({
      id: "entity-point-light-main",
      position: {
        x: 2,
        y: 3,
        z: -1
      },
      distance: 8
    });
    const document = {
      ...createEmptySceneDocument(),
      entities: {
        [pointLight.id]: pointLight
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "entities",
      ids: [pointLight.id]
    });

    expect(focusTarget).toEqual({
      center: {
        x: 2,
        y: 3,
        z: -1
      },
      radius: 8
    });
  });

  it("frames the selected Camera Rig helper", () => {
    const cameraRig = createCameraRigEntity({
      id: "entity-camera-rig-focus",
      position: {
        x: -3,
        y: 2,
        z: 5
      }
    });
    const document = {
      ...createEmptySceneDocument(),
      entities: {
        [cameraRig.id]: cameraRig
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "entities",
      ids: [cameraRig.id]
    });

    expect(focusTarget?.center.x).toBeCloseTo(-3);
    expect(focusTarget?.center.y).toBeCloseTo(2.28);
    expect(focusTarget?.center.z).toBeCloseTo(5);
    expect(focusTarget?.radius).toBeGreaterThan(0.45);
  });

  it("frames the selected rail Camera Rig helper from its resolved path position", () => {
    const path = createScenePath({
      id: "path-camera-rig-focus-rail",
      points: [
        {
          id: "point-a",
          position: {
            x: 0,
            y: 2,
            z: 0
          }
        },
        {
          id: "point-b",
          position: {
            x: 10,
            y: 2,
            z: 0
          }
        }
      ]
    });
    const cameraRig = createCameraRigEntity({
      id: "entity-camera-rig-focus-rail",
      rigType: "rail",
      pathId: path.id,
      target: {
        kind: "worldPoint",
        point: {
          x: 3,
          y: 1,
          z: 4
        }
      }
    });
    const document = {
      ...createEmptySceneDocument(),
      paths: {
        [path.id]: path
      },
      entities: {
        [cameraRig.id]: cameraRig
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "entities",
      ids: [cameraRig.id]
    });

    expect(focusTarget?.center.x).toBeCloseTo(3);
    expect(focusTarget?.center.y).toBeCloseTo(2.28);
    expect(focusTarget?.center.z).toBeCloseTo(0);
    expect(focusTarget?.radius).toBeGreaterThan(0.45);
  });

  it("frames the selected Path around its authored point bounds", () => {
    const path = createScenePath({
      id: "path-focus",
      points: [
        {
          id: "point-a",
          position: {
            x: -2,
            y: 0,
            z: 1
          }
        },
        {
          id: "point-b",
          position: {
            x: 4,
            y: 2,
            z: 3
          }
        }
      ]
    });
    const document = {
      ...createEmptySceneDocument(),
      paths: {
        [path.id]: path
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "paths",
      ids: [path.id]
    });

    expect(focusTarget).toEqual({
      center: {
        x: 1,
        y: 1,
        z: 2
      },
      radius: Math.hypot(6, 2, 2) * 0.5
    });
  });

  it("frames the selected terrain from its authored grid bounds", () => {
    const terrain = createTerrain({
      id: "terrain-focus",
      position: {
        x: -4,
        y: 2,
        z: -2
      },
      sampleCountX: 3,
      sampleCountZ: 2,
      cellSize: 2,
      heights: [0, 1, 2, -1, 0, 1]
    });
    const document = {
      ...createEmptySceneDocument(),
      terrains: {
        [terrain.id]: terrain
      }
    };

    expect(
      resolveViewportFocusTarget(document, {
        kind: "terrains",
        ids: [terrain.id]
      })
    ).toEqual({
      center: {
        x: -2,
        y: 2.5,
        z: -1
      },
      radius: Math.max(0.5, Math.hypot(4, 3, 2) * 0.5)
    });
  });

  it("frames a selected Path Point tightly around its authored position", () => {
    const path = createScenePath({
      id: "path-point-focus",
      points: [
        {
          id: "point-focus-a",
          position: {
            x: -2,
            y: 0,
            z: 1
          }
        },
        {
          id: "point-focus-b",
          position: {
            x: 4,
            y: 2,
            z: 3
          }
        }
      ]
    });
    const document = {
      ...createEmptySceneDocument(),
      paths: {
        [path.id]: path
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "pathPoint",
      pathId: path.id,
      pointId: path.points[1].id
    });

    expect(focusTarget).toEqual({
      center: {
        x: 4,
        y: 2,
        z: 3
      },
      radius: 0.5
    });
  });

  it("frames the selected Spot Light helper", () => {
    const spotLight = createSpotLightEntity({
      id: "entity-spot-light-main",
      position: {
        x: -2,
        y: 4,
        z: 1
      },
      distance: 12
    });
    const document = {
      ...createEmptySceneDocument(),
      entities: {
        [spotLight.id]: spotLight
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "entities",
      ids: [spotLight.id]
    });

    expect(focusTarget).toEqual({
      center: {
        x: -2,
        y: 4,
        z: 1
      },
      radius: 12
    });
  });

  it("frames a selected Trigger Volume around its authored bounds", () => {
    const triggerVolume = createTriggerVolumeEntity({
      id: "entity-trigger-main",
      position: {
        x: 3,
        y: 2,
        z: -1
      },
      size: {
        x: 4,
        y: 6,
        z: 2
      }
    });
    const document = {
      ...createEmptySceneDocument(),
      entities: {
        [triggerVolume.id]: triggerVolume
      }
    };

    const focusTarget = resolveViewportFocusTarget(document, {
      kind: "entities",
      ids: [triggerVolume.id]
    });

    expect(focusTarget).toEqual({
      center: {
        x: 3,
        y: 2,
        z: -1
      },
      radius: Math.hypot(2, 3, 1)
    });
  });

  it("frames multiple selected model instances around their combined authored bounds", () => {
    const modelInstanceA = createModelInstance({
      id: "model-focus-a",
      assetId: "asset-model-focus",
      position: {
        x: 0,
        y: 0,
        z: 0
      },
      scale: {
        x: 2,
        y: 2,
        z: 2
      }
    });
    const modelInstanceB = createModelInstance({
      id: "model-focus-b",
      assetId: "asset-model-focus",
      position: {
        x: 6,
        y: 0,
        z: 0
      },
      scale: {
        x: 2,
        y: 2,
        z: 2
      }
    });
    const document = {
      ...createEmptySceneDocument(),
      modelInstances: {
        [modelInstanceA.id]: modelInstanceA,
        [modelInstanceB.id]: modelInstanceB
      }
    };

    expect(
      resolveViewportFocusTarget(document, {
        kind: "modelInstances",
        ids: [modelInstanceA.id, modelInstanceB.id]
      })
    ).toEqual({
      center: {
        x: 3,
        y: 0,
        z: 0
      },
      radius: Math.hypot(8, 2, 2) * 0.5
    });
  });

  it("frames the authored scene when nothing is selected and returns null when the scene is empty", () => {
    const brush = createBoxBrush({
      id: "brush-room"
    });
    const populatedDocument = {
      ...createEmptySceneDocument(),
      brushes: {
        [brush.id]: brush
      }
    };

    expect(resolveViewportFocusTarget(populatedDocument, { kind: "none" })).toEqual({
      center: {
        x: 0,
        y: 1,
        z: 0
      },
      radius: Math.hypot(2, 2, 2) * 0.5
    });
    expect(resolveViewportFocusTarget(createEmptySceneDocument(), { kind: "none" })).toBeNull();
  });
});
