import {
  AdditiveAnimationBlendMode,
  type AnimationAction,
  AnimationMixer,
  AnimationUtils,
  Color,
  LoopOnce,
  type Mesh,
  MeshNormalMaterial,
  MeshStandardMaterial,
  Object3D,
  TextureLoader,
  WebGLRenderer,
} from "three";
import { type GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";

import AssetsProvider from "@/assets/utils/AssetsProvider";
import type { QuizSectionItemModel3D, SunoSong } from "@/types";

import { type InterfaceObject3DStep } from "./InterfaceObject3DStep";

export default class Object3DStep
  extends Object3D
  implements InterfaceObject3DStep
{
  private modelData: QuizSectionItemModel3D | undefined;

  private songCoverMaterial!: MeshStandardMaterial;

  private cd: Mesh | undefined;
  rotateCD: boolean = false;

  private animationFadeIn: AnimationAction | undefined;
  private animationFadeOut: AnimationAction | undefined;
  private animationLoop: AnimationAction | undefined;
  private animationError: AnimationAction | undefined;

  private mesh: Object3D = new Object3D();
  private mixer: AnimationMixer | undefined;

  private cdRotMaxSpeed: number = 0.09;
  private cdRotSpeed: number = 0.0;
  private cdRotAcceleration: number = 0.004;
  private cdRotDeceleration: number = 0.001;

  constructor(
    modelData: QuizSectionItemModel3D,
    fadeInOnLoad: boolean = false,
    needsClone: boolean = false,
    isWebGL2: boolean,
  ) {
    super();
    this.modelData = modelData;

    const gltf: GLTF = AssetsProvider.getAsset(
      this.modelData?.src as string,
    )?.asset;

    if (needsClone) gltf.scene = gltf.scene.clone();

    gltf.scene.traverse((child) => {
      const mesh = child as Mesh;
      if (mesh.isMesh) {
        // console.log(mesh);
        // console.log("isWebGL2: ", isWebGL2);

        if (!isWebGL2) {
          const fallBackMaterial = new MeshStandardMaterial({
            color: new Color(0x7683ca),
            roughness: 0.5,
            metalness: 0.5,
            emissive: 0,
            envMapIntensity: 1,
            transparent: true,
            opacity: 0.4,
            blendColor: 0,
          });
          mesh.material = new MeshNormalMaterial();
        } else {
          if (mesh.name === "CD_Mesh_2") {
            this.cd = mesh;
            this.cd.rotation.z = Math.PI;
            this.cd.material = (
              this.cd.material as MeshStandardMaterial
            ).clone();
            this.songCoverMaterial = this.cd.material as MeshStandardMaterial;

            if (!this.modelData?.songCoverTexture) {
            } else {
              this.songCoverMaterial.emissiveMap = this.songCoverMaterial.map =
                null;
              this.songCoverMaterial.color = new Color(0x8990ed);
            }
            this.songCoverMaterial.needsUpdate = true;
          }
        }
      }
    });
    this.mesh = gltf.scene as Object3D;

    this.mesh.position.y = 0.54;
    this.mesh.scale.set(0, 0, 0);

    this.mesh.renderOrder = 1;

    this.mixer = new AnimationMixer(this.mesh);

    if (this.mixer) {
      const animationLoopClip = gltf.animations.find(
        (x) => this.modelData?.loop === x?.name,
      );
      if (animationLoopClip) {
        this.animationLoop = this.mixer.clipAction(animationLoopClip);

        AnimationUtils.makeClipAdditive(animationLoopClip);
        this.animationLoop.blendMode = AdditiveAnimationBlendMode;
      }

      const animationFadeInClip = gltf.animations.find(
        (x) => this.modelData?.transitionIn === x?.name,
      );
      if (animationFadeInClip) {
        this.animationFadeIn = this.mixer.clipAction(animationFadeInClip);
        this.animationFadeIn.loop = LoopOnce;
        this.animationFadeIn.clampWhenFinished = true;
      }

      const animationFadeOutClip = gltf.animations.find(
        (x) => this.modelData?.transitionOut === x?.name,
      );

      if (animationFadeOutClip) {
        this.animationFadeOut = this.mixer.clipAction(animationFadeOutClip);
        this.animationFadeOut.loop = LoopOnce;
        this.animationFadeOut.setEffectiveTimeScale(2.5);
        AnimationUtils.makeClipAdditive(animationFadeOutClip);
        this.animationFadeOut.blendMode = AdditiveAnimationBlendMode;
      }

      const animationErrorClip = gltf.animations.find(
        (x) => "Error" === x?.name,
      );
      if (animationErrorClip) {
        this.animationError = this.mixer.clipAction(animationErrorClip);
        this.animationError.loop = LoopOnce;
        this.animationError.setEffectiveWeight(0.2);
        this.animationError.clampWhenFinished = true;

        AnimationUtils.makeClipAdditive(animationErrorClip);
        this.animationError.blendMode = AdditiveAnimationBlendMode;
      }

      this.mixer.addEventListener("finished", (animation: any) => {
        switch (animation.action._clip.name) {
          case this.modelData?.transitionIn:
            break;

          case this.modelData?.transitionOut:
            this.animationFadeOut?.stop();
            this.animationLoop?.stop();
            this.remove(this.mesh);
            break;
        }
      }); //*/

      if (fadeInOnLoad) {
        this.fadeIn(0.5);
      }
    }
  }

  fadeIn(delay: number = 0) {
    this.add(this.mesh);

    this.mesh.scale.set(0, 0, 0);

    this.animationLoop?.play();

    setTimeout(() => {
      const scale = 0.88;
      this.mesh.scale.set(scale, scale, scale);

      if (this.animationLoop) {
        this.animationLoop.stop();
        this.animationLoop.play();
      }

      if (this.animationFadeIn) {
        if (this.modelData?.type === "CarouselCDs") {
          this.animationFadeIn!.clampWhenFinished = true;
        }
        this.animationFadeIn.stop();
        this.animationFadeIn.play();
      }
    }, delay * 1000);
  }

  fadeOut() {
    if (this.animationFadeOut) {
      this.animationFadeOut.stop();
      this.animationFadeOut.play();
    }
  }

  showError() {
    if (this.animationError) {
      this.animationError.stop();
      this.animationError.play();
    }
  }

  update(delta: number) {
    this.mixer?.update(delta);

    if (this.rotateCD) {
      if (this.cdRotSpeed < this.cdRotMaxSpeed)
        this.cdRotSpeed += this.cdRotAcceleration;
    } else {
      if (this.cdRotSpeed > 0.0) this.cdRotSpeed -= this.cdRotDeceleration;
    }

    this.cd && (this.cd.rotation.z -= this.cdRotSpeed);
  }
  setResultCovers(songs: Array<SunoSong>) {}

  setSong(song: SunoSong, direction: number) {}

  setIsMuted(isMuted: boolean) {}

  onScrollResults(scroll: number) {}

  setResultCover(img: string) {
    const emissiveMap = this.songCoverMaterial.emissiveMap?.source.data.src;

    if (
      !this.modelData?.songCoverTexture ||
      !img ||
      (emissiveMap && emissiveMap === img)
    )
      return;

    new TextureLoader().load(
      img,
      (texture) => {
        this.songCoverMaterial.color = new Color(0x000000);
        this.songCoverMaterial.emissiveMap = texture;
        this.songCoverMaterial.emissiveIntensity = 2;
        this.songCoverMaterial.metalness = 1.0;
        this.songCoverMaterial.roughness = 0.8;
        this.cd && (this.cd.rotation.z = Math.PI);
      },

      undefined,

      function (err) {
        console.error("An error happened.", err);
      },
    );
  }
}
