/** @import { Vector3 } from "@minecraft/server" */

/**
 * A 3D vector class for mathematical operations.
 */
export default class V3 {
    /** @type {number} */ x;
    /** @type {number} */ y;
    /** @type {number} */ z;

    /**
     * Creates a new V3 instance.
     * @param {number|{x: number, y: number, z: number}|[number, number, number]} arg1 X coordinate, object with x, y, z, or array [x, y, z].
     * @param {number} [arg2] Y coordinate (if arg1 is a number).
     * @param {number} [arg3] Z coordinate (if arg1 is a number).
     */
    constructor(arg1, arg2, arg3) {
        if (typeof arg1 === "number" && typeof arg2 === "number" && typeof arg3 === "number") {
            this.x = arg1;
            this.y = arg2;
            this.z = arg3;
        } else if (Array.isArray(arg1)) {
            [this.x, this.y, this.z] = arg1;
        } else if (typeof arg1 === "object" && arg1 !== null && "x" in arg1 && "y" in arg1 && "z" in arg1) {
            this.x = arg1.x;
            this.y = arg1.y;
            this.z = arg1.z;
        } else {
            this.x = 0;
            this.y = 0;
            this.z = 0;
        }
    }

    /**
     * Adds another vector.
     * @param {V3|Vector3} vec The vector to add.
     * @returns {V3} The resulting vector.
     */
    add(vec) {
        return new V3(this.x + vec.x, this.y + vec.y, this.z + vec.z);
    }

    /**
     * Subtracts another vector.
     * @param {V3|Vector3} vec The vector to subtract.
     * @returns {V3} The resulting vector.
     */
    subtract(vec) {
        return new V3(this.x - vec.x, this.y - vec.y, this.z - vec.z);
    }

    /**
     * Scales the vector by a scalar.
     * @param {number} scalar The scalar value.
     * @returns {V3} The scaled vector.
     */
    scale(scalar) {
        return new V3(this.x * scalar, this.y * scalar, this.z * scalar);
    }

    /**
     * Calculates the dot product with another vector.
     * @param {V3|Vector3} vec The other vector.
     * @returns {number} The dot product.
     */
    dot(vec) {
        return this.x * vec.x + this.y * vec.y + this.z * vec.z;
    }

    /**
     * Calculates the cross product with another vector.
     * @param {V3|Vector3} vec The other vector.
     * @returns {V3} The cross product vector.
     */
    cross(vec) {
        return new V3(this.y * vec.z - this.z * vec.y, this.z * vec.x - this.x * vec.z, this.x * vec.y - this.y * vec.x);
    }

    /**
     * Calculates the magnitude (length) of the vector.
     * @returns {number} The magnitude.
     */
    magnitude() {
        return Math.sqrt(this.x ** 2 + this.y ** 2 + this.z ** 2);
    }

    /**
     * Normalizes the vector (makes it unit length).
     * @returns {V3} The normalized vector.
     */
    normalize() {
        const mag = this.magnitude();
        return mag === 0 ? new V3(0, 0, 0) : this.scale(1 / mag);
    }

    /**
     * Calculates the distance to another vector.
     * @param {V3|Vector3} vec The other vector.
     * @returns {number} The distance.
     */
    distanceTo(vec) {
        return Math.sqrt((this.x - vec.x) ** 2 + (this.y - vec.y) ** 2 + (this.z - vec.z) ** 2);
    }

    /**
     * Checks if two vectors are equal.
     * @param {V3|Vector3} vec The other vector.
     * @returns {boolean} True if equal, otherwise false.
     */
    equals(vec) {
        return this.x === vec.x && this.y === vec.y && this.z === vec.z;
    }

    /**
     * Converts the vector to an array.
     * @returns {[number, number, number]} The array representation.
     */
    toArray() {
        return [this.x, this.y, this.z];
    }

    /**
     * Converts the vector to a string.
     * @returns {string} The string representation.
     */
    toString() {
        return `V3(${this.x}, ${this.y}, ${this.z})`;
    }
}
