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

import { createEditorStore } from "../../src/app/editor-store";
import { createImportModelAssetCommand } from "../../src/commands/import-model-asset-command";
import { createUpsertModelInstanceCommand } from "../../src/commands/upsert-model-instance-command";
import { createModelInstance } from "../../src/assets/model-instances";
import { createProjectAssetStorageKey, type ModelAssetRecord } from "../../src/assets/project-assets";
import { createEmptySceneDocument } from "../../src/document/scene-document";

describe("model instance commands", () => {
  const modelAsset = {
    id: "asset-model-triangle",
    kind: "model",
    sourceName: "tiny-triangle.gltf",
    mimeType: "model/gltf+json",
    storageKey: createProjectAssetStorageKey("asset-model-triangle"),
    byteLength: 36,
    metadata: {
      kind: "model",
      format: "gltf",
      sceneName: "Fixture Triangle Scene",
      nodeCount: 2,
      meshCount: 1,
      materialNames: ["Fixture Material"],
      textureNames: [],
      animationNames: [],
      boundingBox: {
        min: {
          x: 0,
          y: 0,
          z: 0
        },
        max: {
          x: 1,
          y: 1,
          z: 0
        },
        size: {
          x: 1,
          y: 1,
          z: 0
        }
      },
      warnings: []
    }
  } satisfies ModelAssetRecord;

  it("imports a model asset and placed model instance through undo and redo", () => {
    const store = createEditorStore();
    const modelInstance = createModelInstance({
      id: "model-instance-triangle",
      assetId: modelAsset.id,
      name: "Fixture Triangle",
      position: {
        x: 4,
        y: 2,
        z: -3
      },
      rotationDegrees: {
        x: 0,
        y: 45,
        z: 0
      },
      scale: {
        x: 1.5,
        y: 2,
        z: 1.5
      }
    });

    store.executeCommand(
      createImportModelAssetCommand({
        asset: modelAsset,
        modelInstance,
        label: "Import fixture triangle"
      })
    );

    expect(store.getState().document.assets[modelAsset.id]).toEqual(modelAsset);
    expect(store.getState().document.modelInstances[modelInstance.id]).toEqual(modelInstance);
    expect(store.getState().selection).toEqual({
      kind: "modelInstances",
      ids: [modelInstance.id]
    });

    expect(store.undo()).toBe(true);
    expect(store.getState().document.assets).toEqual({});
    expect(store.getState().document.modelInstances).toEqual({});

    expect(store.redo()).toBe(true);
    expect(store.getState().document.assets[modelAsset.id]).toEqual(modelAsset);
    expect(store.getState().document.modelInstances[modelInstance.id]).toEqual(modelInstance);
  });

  it("updates an existing model instance transform without changing the asset reference", () => {
    const existingModelInstance = createModelInstance({
      id: "model-instance-triangle",
      assetId: modelAsset.id,
      position: {
        x: 1,
        y: 0,
        z: 1
      },
      rotationDegrees: {
        x: 0,
        y: 0,
        z: 0
      },
      scale: {
        x: 1,
        y: 1,
        z: 1
      }
    });
    const store = createEditorStore({
      initialDocument: {
        ...createEmptySceneDocument({ name: "Model Instance Scene" }),
        assets: {
          [modelAsset.id]: modelAsset
        },
        modelInstances: {
          [existingModelInstance.id]: existingModelInstance
        }
      }
    });
    const updatedModelInstance = createModelInstance({
      id: existingModelInstance.id,
      assetId: modelAsset.id,
      name: existingModelInstance.name,
      position: {
        x: 5,
        y: 1,
        z: -2
      },
      rotationDegrees: {
        x: 15,
        y: 90,
        z: 0
      },
      scale: {
        x: 2,
        y: 2,
        z: 2
      }
    });

    store.executeCommand(
      createUpsertModelInstanceCommand({
        modelInstance: updatedModelInstance,
        label: "Update fixture triangle"
      })
    );

    expect(store.getState().document.modelInstances[existingModelInstance.id]).toEqual(updatedModelInstance);
    expect(store.getState().document.assets[modelAsset.id]).toEqual(modelAsset);

    expect(store.undo()).toBe(true);
    expect(store.getState().document.modelInstances[existingModelInstance.id]).toEqual(existingModelInstance);

    expect(store.redo()).toBe(true);
    expect(store.getState().document.modelInstances[existingModelInstance.id]).toEqual(updatedModelInstance);
  });
});
