/**
 * Mock Roblox API for Live Preview
 * Provides a simplified simulation of Roblox's API surface
 */

import { Vector3, Color, Euler, Quaternion } from 'three';

// Type definitions for the mock API
export interface Instance {
  ClassName: string;
  Name: string;
  Parent: Instance | null;
  children: Instance[];
  properties: Record<string, any>;

  // Methods
  Destroy: () => void;
  Clone: () => Instance;
  FindFirstChild: (name: string) => Instance | null;
  GetChildren: () => Instance[];
  IsA: (className: string) => boolean;
  WaitForChild: (name: string, timeout?: number) => Promise<Instance>;
}

export interface Vector3Value {
  X: number;
  Y: number;
  Z: number;
  add: (other: Vector3Value) => Vector3Value;
  sub: (other: Vector3Value) => Vector3Value;
  mul: (scalar: number) => Vector3Value;
  div: (scalar: number) => Vector3Value;
  Magnitude: number;
  Unit: Vector3Value;
  Dot: (other: Vector3Value) => number;
  Cross: (other: Vector3Value) => Vector3Value;
}

export interface Color3Value {
  R: number;
  G: number;
  B: number;
}

export interface CFrameValue {
  Position: Vector3Value;
  LookVector: Vector3Value;
  RightVector: Vector3Value;
  UpVector: Vector3Value;
  Rotation: Vector3Value;
}

// Create Vector3 constructor
export function createVector3(x: number = 0, y: number = 0, z: number = 0): Vector3Value {
  const vec: Vector3Value = {
    X: x,
    Y: y,
    Z: z,
    add: (other) => createVector3(x + other.X, y + other.Y, z + other.Z),
    sub: (other) => createVector3(x - other.X, y - other.Y, z - other.Z),
    mul: (scalar) => createVector3(x * scalar, y * scalar, z * scalar),
    div: (scalar) => createVector3(x / scalar, y / scalar, z / scalar),
    get Magnitude() {
      return Math.sqrt(x * x + y * y + z * z);
    },
    get Unit() {
      const mag = Math.sqrt(x * x + y * y + z * z);
      return mag > 0 ? createVector3(x / mag, y / mag, z / mag) : createVector3();
    },
    Dot: (other) => x * other.X + y * other.Y + z * other.Z,
    Cross: (other) => createVector3(
      y * other.Z - z * other.Y,
      z * other.X - x * other.Z,
      x * other.Y - y * other.X
    ),
  };
  return vec;
}

// Create Color3 constructor
export function createColor3(r: number = 0, g: number = 0, b: number = 0): Color3Value {
  return { R: r, G: g, B: b };
}

export function createColor3FromRGB(r: number, g: number, b: number): Color3Value {
  return { R: r / 255, G: g / 255, B: b / 255 };
}

// Create CFrame constructor
export function createCFrame(x: number = 0, y: number = 0, z: number = 0): CFrameValue {
  return {
    Position: createVector3(x, y, z),
    LookVector: createVector3(0, 0, -1),
    RightVector: createVector3(1, 0, 0),
    UpVector: createVector3(0, 1, 0),
    Rotation: createVector3(0, 0, 0),
  };
}

// Instance factory
let instanceIdCounter = 0;

export function createInstance(className: string, parent?: Instance | null): Instance {
  const instance: Instance = {
    ClassName: className,
    Name: className + instanceIdCounter++,
    Parent: parent || null,
    children: [],
    properties: getDefaultProperties(className),

    Destroy() {
      if (this.Parent) {
        const idx = this.Parent.children.indexOf(this);
        if (idx > -1) this.Parent.children.splice(idx, 1);
      }
      this.children.forEach(child => child.Destroy());
    },

    Clone() {
      const clone = createInstance(this.ClassName);
      clone.Name = this.Name;
      clone.properties = { ...this.properties };
      this.children.forEach(child => {
        const childClone = child.Clone();
        childClone.Parent = clone;
        clone.children.push(childClone);
      });
      return clone;
    },

    FindFirstChild(name: string) {
      return this.children.find(c => c.Name === name) || null;
    },

    GetChildren() {
      return [...this.children];
    },

    IsA(className: string) {
      return this.ClassName === className || getClassHierarchy(this.ClassName).includes(className);
    },

    async WaitForChild(name: string, timeout: number = 5) {
      const existing = this.FindFirstChild(name);
      if (existing) return existing;

      return new Promise((resolve, reject) => {
        const start = Date.now();
        const check = () => {
          const child = this.FindFirstChild(name);
          if (child) {
            resolve(child);
          } else if (Date.now() - start > timeout * 1000) {
            reject(new Error(`WaitForChild timeout: ${name}`));
          } else {
            setTimeout(check, 100);
          }
        };
        check();
      });
    },
  };

  if (parent) {
    parent.children.push(instance);
  }

  return instance;
}

// Default properties for common classes
function getDefaultProperties(className: string): Record<string, any> {
  const defaults: Record<string, Record<string, any>> = {
    Part: {
      Position: createVector3(0, 0, 0),
      Size: createVector3(4, 1, 2),
      Color: createColor3(0.6, 0.6, 0.6),
      BrickColor: 'Medium stone grey',
      Transparency: 0,
      Anchored: false,
      CanCollide: true,
      Material: 'Plastic',
      Shape: 'Block',
    },
    SpawnLocation: {
      Position: createVector3(0, 1, 0),
      Size: createVector3(6, 1, 6),
      Color: createColor3(0.05, 0.5, 0.05),
      Anchored: true,
      CanCollide: true,
    },
    Model: {
      PrimaryPart: null,
    },
    Script: {
      Source: '',
      Enabled: true,
    },
    LocalScript: {
      Source: '',
      Enabled: true,
    },
    ModuleScript: {
      Source: '',
    },
    PointLight: {
      Brightness: 1,
      Color: createColor3(1, 1, 1),
      Range: 8,
      Shadows: false,
    },
    SpotLight: {
      Brightness: 1,
      Color: createColor3(1, 1, 1),
      Range: 16,
      Angle: 90,
      Shadows: true,
    },
    SurfaceGui: {
      Face: 'Front',
      Enabled: true,
    },
    TextLabel: {
      Text: 'Label',
      TextColor3: createColor3(0, 0, 0),
      TextSize: 14,
      BackgroundTransparency: 0,
      BackgroundColor3: createColor3(1, 1, 1),
    },
    Sound: {
      SoundId: '',
      Volume: 0.5,
      Playing: false,
      Looped: false,
    },
    ParticleEmitter: {
      Enabled: true,
      Rate: 20,
      Lifetime: { Min: 1, Max: 2 },
      Speed: { Min: 5, Max: 5 },
      Color: createColor3(1, 1, 1),
    },
    Humanoid: {
      Health: 100,
      MaxHealth: 100,
      WalkSpeed: 16,
      JumpPower: 50,
    },
    Camera: {
      CFrame: createCFrame(0, 5, 10),
      FieldOfView: 70,
      CameraType: 'Custom',
    },
  };

  return defaults[className] || {};
}

// Class hierarchy for IsA checks
function getClassHierarchy(className: string): string[] {
  const hierarchies: Record<string, string[]> = {
    Part: ['BasePart', 'PVInstance', 'Instance'],
    SpawnLocation: ['Part', 'BasePart', 'PVInstance', 'Instance'],
    Model: ['PVInstance', 'Instance'],
    Script: ['BaseScript', 'LuaSourceContainer', 'Instance'],
    LocalScript: ['BaseScript', 'LuaSourceContainer', 'Instance'],
    ModuleScript: ['LuaSourceContainer', 'Instance'],
    Humanoid: ['Instance'],
    Sound: ['Instance'],
    PointLight: ['Light', 'Instance'],
    SpotLight: ['Light', 'Instance'],
    Camera: ['Instance'],
  };

  return hierarchies[className] || ['Instance'];
}

// Services
export interface GameService {
  Name: string;
  instances: Record<string, Instance>;
}

export function createGameServices() {
  const workspace = createInstance('Workspace');
  workspace.Name = 'Workspace';

  const replicatedStorage = createInstance('ReplicatedStorage');
  replicatedStorage.Name = 'ReplicatedStorage';

  const players = createInstance('Players');
  players.Name = 'Players';

  const lighting = createInstance('Lighting');
  lighting.Name = 'Lighting';
  lighting.properties = {
    Ambient: createColor3(0.5, 0.5, 0.5),
    Brightness: 2,
    ClockTime: 14,
    GeographicLatitude: 41.7,
    GlobalShadows: true,
    OutdoorAmbient: createColor3(0.5, 0.5, 0.5),
  };

  const serverStorage = createInstance('ServerStorage');
  serverStorage.Name = 'ServerStorage';

  const starterGui = createInstance('StarterGui');
  starterGui.Name = 'StarterGui';

  const starterPack = createInstance('StarterPack');
  starterPack.Name = 'StarterPack';

  const starterPlayer = createInstance('StarterPlayer');
  starterPlayer.Name = 'StarterPlayer';

  return {
    Workspace: workspace,
    ReplicatedStorage: replicatedStorage,
    Players: players,
    Lighting: lighting,
    ServerStorage: serverStorage,
    StarterGui: starterGui,
    StarterPack: starterPack,
    StarterPlayer: starterPlayer,
  };
}

// Events system
export type EventCallback = (...args: any[]) => void;

export interface RBXScriptSignal {
  Connect: (callback: EventCallback) => { Disconnect: () => void };
  Wait: () => Promise<any[]>;
  callbacks: EventCallback[];
  fire: (...args: any[]) => void;
}

export function createSignal(): RBXScriptSignal {
  const callbacks: EventCallback[] = [];

  return {
    callbacks,
    Connect(callback: EventCallback) {
      callbacks.push(callback);
      return {
        Disconnect: () => {
          const idx = callbacks.indexOf(callback);
          if (idx > -1) callbacks.splice(idx, 1);
        },
      };
    },
    Wait() {
      return new Promise((resolve) => {
        const handler = (...args: any[]) => {
          const idx = callbacks.indexOf(handler);
          if (idx > -1) callbacks.splice(idx, 1);
          resolve(args);
        };
        callbacks.push(handler);
      });
    },
    fire(...args: any[]) {
      callbacks.forEach(cb => {
        try {
          cb(...args);
        } catch (e) {
          console.error('Event callback error:', e);
        }
      });
    },
  };
}

// TweenService mock
export interface TweenInfo {
  Time: number;
  EasingStyle: string;
  EasingDirection: string;
  RepeatCount: number;
  Reverses: boolean;
  DelayTime: number;
}

export function createTweenInfo(
  time: number = 1,
  easingStyle: string = 'Linear',
  easingDirection: string = 'Out',
  repeatCount: number = 0,
  reverses: boolean = false,
  delayTime: number = 0
): TweenInfo {
  return { Time: time, EasingStyle: easingStyle, EasingDirection: easingDirection, RepeatCount: repeatCount, Reverses: reverses, DelayTime: delayTime };
}

export interface Tween {
  Play: () => void;
  Pause: () => void;
  Cancel: () => void;
  Completed: RBXScriptSignal;
}

export function createTweenService() {
  return {
    Create(instance: Instance, tweenInfo: TweenInfo, properties: Record<string, any>): Tween {
      const completed = createSignal();
      let animationId: number | null = null;
      let isPaused = false;

      return {
        Completed: completed,
        Play() {
          if (isPaused) {
            isPaused = false;
            return;
          }

          const startValues: Record<string, any> = {};
          const endValues = properties;

          for (const key in endValues) {
            startValues[key] = instance.properties[key];
          }

          const duration = tweenInfo.Time * 1000;
          const startTime = Date.now();

          const animate = () => {
            if (isPaused) return;

            const elapsed = Date.now() - startTime;
            const progress = Math.min(elapsed / duration, 1);

            // Apply easing
            const easedProgress = applyEasing(progress, tweenInfo.EasingStyle, tweenInfo.EasingDirection);

            // Interpolate values
            for (const key in endValues) {
              const start = startValues[key];
              const end = endValues[key];

              if (typeof start === 'number' && typeof end === 'number') {
                instance.properties[key] = start + (end - start) * easedProgress;
              } else if (start && typeof start === 'object' && 'X' in start) {
                // Vector3
                instance.properties[key] = createVector3(
                  start.X + (end.X - start.X) * easedProgress,
                  start.Y + (end.Y - start.Y) * easedProgress,
                  start.Z + (end.Z - start.Z) * easedProgress
                );
              } else if (start && typeof start === 'object' && 'R' in start) {
                // Color3
                instance.properties[key] = createColor3(
                  start.R + (end.R - start.R) * easedProgress,
                  start.G + (end.G - start.G) * easedProgress,
                  start.B + (end.B - start.B) * easedProgress
                );
              }
            }

            if (progress < 1) {
              animationId = requestAnimationFrame(animate);
            } else {
              completed.fire();
            }
          };

          animate();
        },
        Pause() {
          isPaused = true;
        },
        Cancel() {
          if (animationId) {
            cancelAnimationFrame(animationId);
          }
        },
      };
    },
  };
}

function applyEasing(t: number, style: string, direction: string): number {
  // Simplified easing functions
  const easingFunctions: Record<string, (t: number) => number> = {
    Linear: (t) => t,
    Quad: (t) => t * t,
    Cubic: (t) => t * t * t,
    Sine: (t) => 1 - Math.cos((t * Math.PI) / 2),
    Bounce: (t) => {
      if (t < 1 / 2.75) return 7.5625 * t * t;
      if (t < 2 / 2.75) return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;
      if (t < 2.5 / 2.75) return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;
      return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;
    },
    Elastic: (t) => t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * (2 * Math.PI) / 3),
  };

  const ease = easingFunctions[style] || easingFunctions.Linear;

  if (direction === 'In') {
    return ease(t);
  } else if (direction === 'Out') {
    return 1 - ease(1 - t);
  } else if (direction === 'InOut') {
    return t < 0.5 ? ease(t * 2) / 2 : 1 - ease((1 - t) * 2) / 2;
  }

  return t;
}

// RunService mock
export function createRunService() {
  const heartbeat = createSignal();
  const renderStepped = createSignal();
  const stepped = createSignal();

  let lastTime = Date.now();
  let isRunning = false;

  const tick = () => {
    if (!isRunning) return;

    const now = Date.now();
    const dt = (now - lastTime) / 1000;
    lastTime = now;

    stepped.fire(now / 1000, dt);
    heartbeat.fire(dt);
    renderStepped.fire(dt);

    requestAnimationFrame(tick);
  };

  return {
    Heartbeat: heartbeat,
    RenderStepped: renderStepped,
    Stepped: stepped,
    start() {
      isRunning = true;
      lastTime = Date.now();
      requestAnimationFrame(tick);
    },
    stop() {
      isRunning = false;
    },
  };
}

// UserInputService mock
export function createUserInputService() {
  const inputBegan = createSignal();
  const inputEnded = createSignal();
  const inputChanged = createSignal();

  return {
    InputBegan: inputBegan,
    InputEnded: inputEnded,
    InputChanged: inputChanged,
    GetMouseLocation: () => ({ X: 0, Y: 0 }),
    IsKeyDown: (keyCode: string) => false,
    IsMouseButtonPressed: (button: number) => false,
  };
}

// Export combined API
export function createRobloxAPI() {
  const services = createGameServices();
  const runService = createRunService();
  const tweenService = createTweenService();
  const userInputService = createUserInputService();

  return {
    game: {
      GetService: (serviceName: string) => {
        switch (serviceName) {
          case 'Workspace': return services.Workspace;
          case 'ReplicatedStorage': return services.ReplicatedStorage;
          case 'Players': return services.Players;
          case 'Lighting': return services.Lighting;
          case 'ServerStorage': return services.ServerStorage;
          case 'StarterGui': return services.StarterGui;
          case 'StarterPack': return services.StarterPack;
          case 'StarterPlayer': return services.StarterPlayer;
          case 'TweenService': return tweenService;
          case 'RunService': return runService;
          case 'UserInputService': return userInputService;
          default: throw new Error(`Unknown service: ${serviceName}`);
        }
      },
      Workspace: services.Workspace,
    },
    workspace: services.Workspace,
    Vector3: {
      new: createVector3,
      zero: createVector3(0, 0, 0),
      one: createVector3(1, 1, 1),
      xAxis: createVector3(1, 0, 0),
      yAxis: createVector3(0, 1, 0),
      zAxis: createVector3(0, 0, 1),
    },
    Color3: {
      new: createColor3,
      fromRGB: createColor3FromRGB,
    },
    CFrame: {
      new: createCFrame,
    },
    TweenInfo: {
      new: createTweenInfo,
    },
    Instance: {
      new: createInstance,
    },
    Enum: {
      Material: {
        Plastic: 'Plastic',
        Wood: 'Wood',
        Metal: 'Metal',
        Glass: 'Glass',
        Neon: 'Neon',
        Grass: 'Grass',
        Sand: 'Sand',
        Brick: 'Brick',
        Concrete: 'Concrete',
        Ice: 'Ice',
        Marble: 'Marble',
        Granite: 'Granite',
        SmoothPlastic: 'SmoothPlastic',
        ForceField: 'ForceField',
      },
      PartType: {
        Block: 'Block',
        Ball: 'Ball',
        Cylinder: 'Cylinder',
        Wedge: 'Wedge',
      },
      KeyCode: {
        W: 'W', A: 'A', S: 'S', D: 'D',
        E: 'E', F: 'F', G: 'G', Q: 'Q', R: 'R',
        Space: 'Space', LeftShift: 'LeftShift', LeftControl: 'LeftControl',
        One: 'One', Two: 'Two', Three: 'Three',
      },
      EasingStyle: {
        Linear: 'Linear', Quad: 'Quad', Cubic: 'Cubic',
        Sine: 'Sine', Bounce: 'Bounce', Elastic: 'Elastic',
      },
      EasingDirection: {
        In: 'In', Out: 'Out', InOut: 'InOut',
      },
    },
    print: (...args: any[]) => console.log('[Roblox]', ...args),
    warn: (...args: any[]) => console.warn('[Roblox]', ...args),
    error: (message: string) => { throw new Error(message); },
    wait: (seconds: number = 0) => new Promise(resolve => setTimeout(resolve, seconds * 1000)),
    task: {
      wait: (seconds: number = 0) => new Promise(resolve => setTimeout(resolve, seconds * 1000)),
      spawn: (fn: () => void) => setTimeout(fn, 0),
      delay: (seconds: number, fn: () => void) => setTimeout(fn, seconds * 1000),
    },
    services,
    runService,
  };
}
