import { fireEvent, render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";

import { ProjectSequencesPanel } from "../../src/app/ProjectSequencesPanel";
import {
  createActiveSceneControlTargetRef,
  getControlTargetRefKey
} from "../../src/controls/control-surface";
import { createProjectSequence } from "../../src/sequencer/project-sequences";

describe("ProjectSequencesPanel", () => {
  it("edits teleport and visibility sequence effects through the sequence editor", () => {
    const sequence = createProjectSequence({
      id: "sequence-main",
      title: "Console Sequence",
      effects: [
        {
          stepClass: "impulse",
          type: "teleportPlayer",
          targetEntityId: "teleport-a"
        },
        {
          stepClass: "impulse",
          type: "setVisibility",
          target: {
            kind: "brush",
            brushId: "brush-a"
          },
          mode: "toggle"
        }
      ]
    });

    const onAddControlEffect = vi.fn();
    const onAddTeleportStep = vi.fn();
    const onAddVisibilityStep = vi.fn();
    const onSetTeleportStepTarget = vi.fn();
    const onSetVisibilityStepTarget = vi.fn();
    const onSetVisibilityStepMode = vi.fn();

    render(
      <ProjectSequencesPanel
        sequences={{
          sequences: {
            [sequence.id]: sequence
          }
        }}
        targetOptions={[
          {
            key: "modelInstance:model-a",
            target: {
              kind: "modelInstance",
              modelInstanceId: "model-a"
            },
            label: "Model A",
            subtitle: "Animation",
            groupLabel: "Model Instances",
            defaults: {
              animationClipNames: ["Idle", "Wave"]
            }
          },
          {
            key: "entity:sound-a",
            target: {
              kind: "entity",
              entityId: "sound-a",
              entityKind: "soundEmitter"
            },
            label: "Sound A",
            subtitle: "Emitter",
            groupLabel: "Sound Emitters",
            defaults: {}
          }
        ]}
        npcTalkTargetOptions={[]}
        teleportTargetOptions={[
          { entityId: "teleport-a", label: "North Gate" },
          { entityId: "teleport-b", label: "South Gate" }
        ]}
        sceneTransitionTargetOptions={[
          {
            targetKey: "scene-house::entry-front",
            label: "House · Front Entry"
          }
        ]}
        visibilityTargetOptions={[
          { targetKey: "brush:brush-a", label: "Wall A" },
          { targetKey: "brush:brush-b", label: "Wall B" }
        ]}
        selectedSequenceId={sequence.id}
        onSelectSequence={() => {}}
        onAddSequence={() => {}}
        onDeleteSequence={() => {}}
        onSetSequenceTitle={() => {}}
        onAddControlEffect={onAddControlEffect}
        onAddNpcTalkEffect={() => {}}
        onAddTeleportStep={onAddTeleportStep}
        onAddSceneTransitionStep={() => {}}
        onAddVisibilityStep={onAddVisibilityStep}
        onDeleteStep={() => {}}
        onSetControlStepTarget={() => {}}
        onSetControlStepEffectOption={() => {}}
        onSetControlStepNumericValue={() => {}}
        onSetControlStepColorValue={() => {}}
        onSetControlStepAnimationClip={() => {}}
        onSetControlStepAnimationLoop={() => {}}
        onSetControlStepPathId={() => {}}
        onSetControlStepPathSpeed={() => {}}
        onSetControlStepPathLoop={() => {}}
        onSetControlStepPathSmooth={() => {}}
        onSetNpcTalkStepNpcEntityId={() => {}}
        onSetNpcTalkStepDialogueId={() => {}}
        onSetTeleportStepTarget={onSetTeleportStepTarget}
        onSetSceneTransitionStepTarget={() => {}}
        onSetVisibilityStepTarget={onSetVisibilityStepTarget}
        onSetVisibilityStepMode={onSetVisibilityStepMode}
      />
    );

    fireEvent.change(screen.getByLabelText("Teleport Target"), {
      target: { value: "teleport-b" }
    });
    fireEvent.change(screen.getByLabelText("Target"), {
      target: { value: "brush:brush-b" }
    });
    fireEvent.change(screen.getByLabelText("Mode"), {
      target: { value: "hide" }
    });

    fireEvent.click(
      screen.getByRole("button", { name: "Add Model A Play Animation Effect" })
    );
    fireEvent.click(screen.getByRole("button", { name: "Add Teleport Effect" }));
    fireEvent.click(screen.getByRole("button", { name: "Add Visibility Effect" }));

    expect(onSetTeleportStepTarget).toHaveBeenCalledWith(
      "sequence-main",
      0,
      "teleport-b"
    );
    expect(onSetVisibilityStepTarget).toHaveBeenCalledWith(
      "sequence-main",
      1,
      "brush:brush-b"
    );
    expect(onSetVisibilityStepMode).toHaveBeenCalledWith(
      "sequence-main",
      1,
      "hide"
    );
    expect(onAddTeleportStep).toHaveBeenCalledWith("sequence-main", "teleport-a");
    expect(onAddVisibilityStep).toHaveBeenCalledWith(
      "sequence-main",
      "brush:brush-a"
    );
    expect(onAddControlEffect).toHaveBeenCalledWith(
      "sequence-main",
      "modelInstance:model-a",
      "model.playAnimation"
    );
  });

  it("edits actor animation and path effects through the sequence editor", () => {
    const onSetControlStepAnimationClip = vi.fn();
    const onSetControlStepAnimationLoop = vi.fn();
    const onSetControlStepPathId = vi.fn();
    const onSetControlStepPathSpeed = vi.fn();
    const onSetControlStepPathLoop = vi.fn();
    const onSetControlStepPathSmooth = vi.fn();

    render(
      <ProjectSequencesPanel
        sequences={{
          sequences: {
            "sequence-actor": createProjectSequence({
              id: "sequence-actor",
              title: "Actor Routine",
              effects: [
                {
                  stepClass: "held",
                  type: "controlEffect",
                  effect: {
                    type: "playActorAnimation",
                    target: {
                      kind: "actor",
                      actorId: "actor-guard"
                    },
                    clipName: "Idle",
                    loop: true
                  }
                },
                {
                  stepClass: "held",
                  type: "controlEffect",
                  effect: {
                    type: "followActorPath",
                    target: {
                      kind: "actor",
                      actorId: "actor-guard"
                    },
                    pathId: "path-a",
                    speed: 1,
                    loop: false,
                    smoothPath: true,
                    progressMode: "deriveFromTime"
                  }
                }
              ]
            })
          }
        }}
        targetOptions={[
          {
            key: "actor:actor-guard",
            target: {
              kind: "actor",
              actorId: "actor-guard"
            },
            label: "Guard",
            subtitle: "NPC",
            groupLabel: "Actors",
            defaults: {
              actorAnimationClipNames: ["Idle", "Wave"],
              actorPathOptions: [
                { pathId: "path-a", label: "Patrol A", loop: false },
                { pathId: "path-b", label: "Patrol B", loop: true }
              ],
              actorPathSpeed: 1.2
            }
          }
        ]}
        npcTalkTargetOptions={[]}
        teleportTargetOptions={[]}
        sceneTransitionTargetOptions={[]}
        visibilityTargetOptions={[]}
        selectedSequenceId="sequence-actor"
        onSelectSequence={() => {}}
        onAddSequence={() => {}}
        onDeleteSequence={() => {}}
        onSetSequenceTitle={() => {}}
        onAddControlEffect={() => {}}
        onAddNpcTalkEffect={() => {}}
        onAddTeleportStep={() => {}}
        onAddSceneTransitionStep={() => {}}
        onAddVisibilityStep={() => {}}
        onDeleteStep={() => {}}
        onSetControlStepTarget={() => {}}
        onSetControlStepEffectOption={() => {}}
        onSetControlStepNumericValue={() => {}}
        onSetControlStepColorValue={() => {}}
        onSetControlStepAnimationClip={onSetControlStepAnimationClip}
        onSetControlStepAnimationLoop={onSetControlStepAnimationLoop}
        onSetControlStepPathId={onSetControlStepPathId}
        onSetControlStepPathSpeed={onSetControlStepPathSpeed}
        onSetControlStepPathLoop={onSetControlStepPathLoop}
        onSetControlStepPathSmooth={onSetControlStepPathSmooth}
        onSetNpcTalkStepNpcEntityId={() => {}}
        onSetNpcTalkStepDialogueId={() => {}}
        onSetTeleportStepTarget={() => {}}
        onSetSceneTransitionStepTarget={() => {}}
        onSetVisibilityStepTarget={() => {}}
        onSetVisibilityStepMode={() => {}}
      />
    );

    fireEvent.change(screen.getByDisplayValue("Idle"), {
      target: { value: "Wave" }
    });
    fireEvent.click(screen.getAllByLabelText("Loop")[0]!);
    fireEvent.change(screen.getByDisplayValue("Patrol A"), {
      target: { value: "path-b" }
    });
    fireEvent.blur(screen.getByDisplayValue("1"), {
      target: { value: "1.6" }
    });
    fireEvent.click(screen.getAllByLabelText("Loop")[1]!);
    fireEvent.click(screen.getByLabelText("Smooth Path"));

    expect(onSetControlStepAnimationClip).toHaveBeenCalledWith(
      "sequence-actor",
      0,
      "Wave"
    );
    expect(onSetControlStepAnimationLoop).toHaveBeenCalledWith(
      "sequence-actor",
      0,
      false
    );
    expect(onSetControlStepPathId).toHaveBeenCalledWith(
      "sequence-actor",
      1,
      "path-b"
    );
    expect(onSetControlStepPathSpeed).toHaveBeenCalledWith(
      "sequence-actor",
      1,
      1.6
    );
    expect(onSetControlStepPathLoop).toHaveBeenCalledWith(
      "sequence-actor",
      1,
      true
    );
    expect(onSetControlStepPathSmooth).toHaveBeenCalledWith(
      "sequence-actor",
      1,
      false
    );
  });

  it("hides target and effect selectors for active scene lighting effects", () => {
    const sceneTarget = createActiveSceneControlTargetRef();

    render(
      <ProjectSequencesPanel
        sequences={{
          sequences: {
            "sequence-scene": createProjectSequence({
              id: "sequence-scene",
              title: "Scene Lighting",
              effects: [
                {
                  stepClass: "held",
                  type: "controlEffect",
                  effect: {
                    type: "setAmbientLightIntensity",
                    target: sceneTarget,
                    intensity: 0.4
                  }
                }
              ]
            })
          }
        }}
        targetOptions={[
          {
            key: getControlTargetRefKey(sceneTarget),
            target: sceneTarget,
            label: "Active Scene Lighting",
            subtitle: "Scene",
            groupLabel: "Scene Lighting",
            defaults: {
              ambientLightIntensity: 1
            }
          }
        ]}
        npcTalkTargetOptions={[]}
        teleportTargetOptions={[]}
        sceneTransitionTargetOptions={[]}
        visibilityTargetOptions={[]}
        selectedSequenceId="sequence-scene"
        onSelectSequence={() => {}}
        onAddSequence={() => {}}
        onDeleteSequence={() => {}}
        onSetSequenceTitle={() => {}}
        onAddControlEffect={() => {}}
        onAddNpcTalkEffect={() => {}}
        onAddTeleportStep={() => {}}
        onAddSceneTransitionStep={() => {}}
        onAddVisibilityStep={() => {}}
        onDeleteStep={() => {}}
        onSetControlStepTarget={() => {}}
        onSetControlStepEffectOption={() => {}}
        onSetControlStepNumericValue={() => {}}
        onSetControlStepColorValue={() => {}}
        onSetControlStepAnimationClip={() => {}}
        onSetControlStepAnimationLoop={() => {}}
        onSetControlStepPathId={() => {}}
        onSetControlStepPathSpeed={() => {}}
        onSetControlStepPathLoop={() => {}}
        onSetControlStepPathSmooth={() => {}}
        onSetNpcTalkStepNpcEntityId={() => {}}
        onSetNpcTalkStepDialogueId={() => {}}
        onSetTeleportStepTarget={() => {}}
        onSetSceneTransitionStepTarget={() => {}}
        onSetVisibilityStepTarget={() => {}}
        onSetVisibilityStepMode={() => {}}
      />
    );

    expect(
      screen.getByText("This effect applies to the active scene lighting.")
    ).toBeVisible();
    expect(screen.queryByLabelText("Target")).toBeNull();
    expect(screen.queryByLabelText("Effect")).toBeNull();
  });
});
