import { AnimationClip } from "three";

import { ScaleClipType } from "../../engine/engine_animation.js";
import { AnimationUtils } from "../../engine/engine_animation.js";
import { serializable } from "../../engine/engine_serialization_decorator.js";
import { registerType } from "../../engine/engine_typestore.js";
import { Animation } from "../Animation.js";
import { Behaviour } from "../Component.js";


/**
 * [HoverAnimation](https://engine.needle.tools/docs/api/HoverAnimation) plays animations in response to pointer hover events on the object this component is attached to.
 * The component automatically detects when the mouse pointer or touch enters/exits the object or any of its children, triggering the corresponding animations.
 *
 * **How It Works:**
 * The component listens to pointer enter and exit events and switches between two animation states:
 * - **Hover state**: Plays when the pointer enters the object (default: scale up to 110%)
 * - **Idle state**: Plays when the pointer exits the object (default: returns to original scale)
 *
 * **Default Behavior:**
 * If no custom animation clips are provided, the component automatically creates a smooth scale-up animation using the
 * {@link type}, {@link duration}, and {@link scaleFactor} properties. This provides instant hover feedback without
 * requiring any animation setup.
 *
 * **Custom Animations:**
 * You can provide your own animation clips for complete control over the hover effect. This allows you to create
 * complex animations involving position, rotation, color changes, or any other animated property.
 *
 * **Common Use Cases:**
 * - Interactive buttons with scale feedback
 * - Product showcases with highlight animations
 * - Menu items with hover effects
 * - Interactive 3D objects in AR/VR experiences
 * - Call-to-action elements with attention-grabbing animations
 *
 * @example Basic usage with default scale animation
 * ```ts
 * const button = new Object3D();
 * button.addComponent(HoverAnimation, {
 *   scaleFactor: 1.2,    // Scale to 120% on hover
 *   duration: 0.2,       // 200ms animation
 *   type: "ease-in-out"  // Smooth easing
 * });
 * scene.add(button);
 * ```
 *
 * @example Custom hover animations
 * ```ts
 * const obj = new Object3D();
 * const hoverAnim = loadAnimationClip("hover-glow.anim");
 * const idleAnim = loadAnimationClip("idle-pulse.anim");
 *
 * obj.addComponent(HoverAnimation, {
 *   hovered: hoverAnim,  // Custom hover animation
 *   idle: idleAnim       // Custom idle animation
 * });
 * scene.add(obj);
 * ```
 *
 * @example Quick scale animation with custom settings
 * ```ts
 * gameObject.addComponent(HoverAnimation, {
 *   scaleFactor: 1.15,
 *   duration: 0.15,
 *   type: "ease-out"
 * });
 * ```
 *
 * @see {@link Animation} - The underlying animation component used to play clips
 * @see {@link AnimationClip} - For creating custom animation clips
 * @see {@link AnimationUtils} - Utility functions for creating animations programmatically
 * @see {@link ScaleClipType} - Available easing types for the default scale animation
 * @see {@link ObjectRaycaster} - Controls which objects receive pointer events
 * @see {@link PointerEvents} - For more complex pointer interaction handling
 *
 * @summary Plays animations on pointer hover enter/exit events
 * @category Interactivity
 * @group Components
 * @component
 */
@registerType
export class HoverAnimation extends Behaviour {

    /**
     * The easing type for the default scale animation.
     *
     * This property controls how the scale animation interpolates from the start to end value.
     * Different easing types create different "feels" for the hover effect.
     *
     * **Available types:**
     * - `"linear"`: Constant speed throughout the animation
     * - `"ease-in"`: Starts slow, ends fast
     * - `"ease-out"`: Starts fast, ends slow (good for responsive feel)
     * - `"ease-in-out"`: Starts slow, fast in middle, ends slow (smooth and natural)
     *
     * **Note:** This is only used when no custom {@link hovered} animation clip is provided.
     * If you provide a custom animation clip, this property is ignored.
     *
     * @see {@link ScaleClipType} for all available easing types
     * @default "linear"
     */
    @serializable()
    type: ScaleClipType = "linear";

    /**
     * Duration of the default hover animation in seconds.
     *
     * This controls how long it takes for the object to scale up when hovered.
     * Shorter durations feel more responsive, while longer durations feel smoother.
     *
     * **Recommendations:**
     * - `0.1-0.15s`: Snappy, responsive feel (good for buttons)
     * - `0.2-0.3s`: Smooth, noticeable animation
     * - `0.4s+`: Slow, emphasized effect
     *
     * **Note:** This is only used when no custom {@link hovered} animation clip is provided.
     * If you provide a custom animation clip, this property is ignored.
     *
     * @default 0.1
     */
    @serializable()
    duration: number = 0.1;

    /**
     * The scale multiplier to apply when the object is hovered.
     *
     * This value is multiplied with the object's original scale to determine the hover size.
     * A value of `1.0` means no change, values greater than `1.0` scale up, and values less than `1.0` scale down.
     *
     * **Examples:**
     * - `1.0`: No scale change
     * - `1.1`: Scale to 110% (subtle effect, default)
     * - `1.2`: Scale to 120% (noticeable effect)
     * - `1.5`: Scale to 150% (dramatic effect)
     * - `0.9`: Scale to 90% (shrink on hover)
     *
     * **Note:** This is only used when no custom {@link hovered} animation clip is provided.
     * If you provide a custom animation clip, this property is ignored.
     *
     * @default 1.1
     */
    @serializable()
    scaleFactor: number = 1.1;


    /**
     * Custom animation clip to play when the pointer hovers over the object.
     *
     * If `null`, the component automatically generates a scale-up animation based on the
     * {@link type}, {@link duration}, and {@link scaleFactor} properties.
     *
     * Provide a custom animation clip if you want more complex hover effects such as:
     * - Color changes or material property animations
     * - Position or rotation changes
     * - Multi-property animations
     * - Animations affecting child objects
     *
     * **Tip:** The animation plays with a 0.1s fade duration for smooth transitions.
     *
     * @see {@link AnimationClip} for creating custom animation clips
     * @see {@link AnimationUtils.createScaleClip} for programmatically creating scale animations
     * @default null (generates default scale animation)
     */
    @serializable(AnimationClip)
    hovered: AnimationClip | null = null;

    /**
     * Custom animation clip to play when the pointer is not hovering over the object (idle state).
     *
     * If `null`, an empty animation clip is used, which returns the object to its original state
     * when the hover animation ends.
     *
     * You can provide a custom idle animation for effects such as:
     * - Subtle breathing or floating motion when not hovered
     * - Pulsing or glowing effects in idle state
     * - Return-to-normal animations with custom easing
     * - Looping ambient animations
     *
     * **Tip:** The idle animation is played with `loop: true`, so it will repeat continuously
     * until the object is hovered again.
     *
     * @see {@link AnimationClip} for creating custom animation clips
     * @see {@link AnimationUtils.emptyClip} to see how the default empty clip is created
     * @default null (uses empty clip that returns to original state)
     */
    @serializable(AnimationClip)
    idle: AnimationClip | null = null;

    private animation: Animation | null = null;

    start() {
        if (!this.idle) this.idle = AnimationUtils.emptyClip();

        if (!this.hovered || !(this.hovered instanceof AnimationClip)) {
            this.hovered = AnimationUtils.createScaleClip({
                type: "linear",
                duration: this.duration || 0.1,
                scale: this.gameObject.scale,
                scaleFactor: this.scaleFactor || 1.1,
            });
        }

        this.animation ??= this.gameObject.addComponent(Animation);
        this.animation.playAutomatically = false;
        this.playIdle();
    }

    onEnable() {
        if (this.animation) this.animation.enabled = true;
        this.playIdle();
    }
    onDisable() {
        if (this.animation) this.animation.enabled = false;
        this.playIdle();
    }

    onPointerEnter() {
        this.playHover();
    }

    onPointerExit() {
        this.playIdle();
    }

    private playIdle() {
        if (this.idle) this.animation?.play(this.idle, { exclusive: true, fadeDuration: .1, loop: true });
    }
    private playHover() {
        if (this.hovered) this.animation?.play(this.hovered, { exclusive: true, fadeDuration: .1, loop: false, clampWhenFinished: true });
    }

}