// src/core/ARSession.ts

import { Camera } from 'expo-camera';
import { EventEmitter } from 'events';
import { CloudAnchorManager, CloudAnchor } from './CloudAnchorManager';
import { UserSessionManager, ARUser } from './UserSessionManager';

export interface ARSessionOptions {
  cameraType?: 'front' | 'back';
  enablePlaneDetection?: boolean;
  enableCloudAnchors?: boolean;
  enableMultiUser?: boolean;
  cloudAnchorOptions?: {
    serverUrl?: string;
    resolveTimeoutSeconds?: number;
  };
  userSessionOptions?: {
    serverUrl?: string;
    heartbeatInterval?: number;
    inactivityTimeout?: number;
  };
}

export enum ARSessionState {
  UNINITIALIZED = 'UNINITIALIZED',
  INITIALIZED = 'INITIALIZED',
  RUNNING = 'RUNNING',
  PAUSED = 'PAUSED',
  STOPPED = 'STOPPED',
}

export class ARSession extends EventEmitter {
  private state: ARSessionState = ARSessionState.UNINITIALIZED;
  private options: ARSessionOptions;
  private cloudAnchorManager: CloudAnchorManager | null = null;
  private userSessionManager: UserSessionManager | null = null;

  constructor(options: ARSessionOptions = {}) {
    super();
    this.options = options;
  }

  /**
   * Initializes the AR session:
   * - Requests camera permissions.
   * - Prepares configuration based on options.
   */
  public async initialize(): Promise<void> {
    try {
      const { status } = await Camera.requestCameraPermissionsAsync();
      if (status !== 'granted') {
        throw new Error('Camera permission not granted');
      }

      if (this.options.enableCloudAnchors) {
        this.cloudAnchorManager = new CloudAnchorManager(this.options.cloudAnchorOptions);
        this.cloudAnchorManager.on('error', (error) => this.emit('error', error));
        this.cloudAnchorManager.on('anchorHosted', (anchor) => this.emit('cloudAnchorHosted', anchor));
        this.cloudAnchorManager.on('anchorResolved', (anchor) => this.emit('cloudAnchorResolved', anchor));
        this.cloudAnchorManager.on('anchorUpdated', (anchor) => this.emit('cloudAnchorUpdated', anchor));
        this.cloudAnchorManager.on('anchorRemoved', (cloudId) => this.emit('cloudAnchorRemoved', cloudId));
      }

      if (this.options.enableMultiUser) {
        this.userSessionManager = new UserSessionManager(this.options.userSessionOptions);
        this.userSessionManager.on('error', (error) => this.emit('error', error));
        this.userSessionManager.on('userJoined', (user) => this.emit('userJoined', user));
        this.userSessionManager.on('userLeft', (user) => this.emit('userLeft', user));
        this.userSessionManager.on('userUpdated', (user) => this.emit('userUpdated', user));
        await this.userSessionManager.initializeSession(this.cloudAnchorManager?.getDeviceId() || 'unknown');
      }

      this.state = ARSessionState.INITIALIZED;
      this.emit('initialized');
      console.log('ARSession initialized with options:', this.options);
    } catch (error) {
      this.emit('error', error);
      console.error('Error initializing ARSession:', error);
      throw error;
    }
  }

  /**
   * Starts the AR session if it is properly initialized.
   */
  public start(): void {
    if (this.state !== ARSessionState.INITIALIZED && this.state !== ARSessionState.PAUSED) {
      console.warn('ARSession must be initialized or paused to start.');
      return;
    }
    this.state = ARSessionState.RUNNING;
    // Start camera feed, rendering, and sensor subscriptions here.
    this.emit('started');
    console.log('ARSession started.');
  }

  /**
   * Pauses the AR session, suspending rendering and sensor updates.
   */
  public pause(): void {
    if (this.state !== ARSessionState.RUNNING) {
      console.warn('ARSession is not running. Cannot pause.');
      return;
    }
    this.state = ARSessionState.PAUSED;
    // Pause camera feed, rendering loop, and sensor updates.
    this.emit('paused');
    console.log('ARSession paused.');
  }

  /**
   * Resumes the AR session if it is paused.
   */
  public resume(): void {
    if (this.state !== ARSessionState.PAUSED) {
      console.warn('ARSession is not paused. Cannot resume.');
      return;
    }
    this.state = ARSessionState.RUNNING;
    // Resume camera feed, rendering loop, and sensor updates.
    this.emit('resumed');
    console.log('ARSession resumed.');
  }

  /**
   * Hosts a local anchor to the cloud
   */
  public async hostCloudAnchor(anchor: any): Promise<CloudAnchor> {
    if (!this.cloudAnchorManager) {
      throw new Error('Cloud anchors are not enabled');
    }
    return this.cloudAnchorManager.hostAnchor(anchor);
  }

  /**
   * Resolves a cloud anchor by its ID
   */
  public async resolveCloudAnchor(cloudId: string): Promise<CloudAnchor> {
    if (!this.cloudAnchorManager) {
      throw new Error('Cloud anchors are not enabled');
    }
    return this.cloudAnchorManager.resolveAnchor(cloudId);
  }

  /**
   * Updates a cloud anchor's position
   */
  public async updateCloudAnchor(cloudId: string, newPosition: { x: number; y: number; z: number }): Promise<CloudAnchor> {
    if (!this.cloudAnchorManager) {
      throw new Error('Cloud anchors are not enabled');
    }
    return this.cloudAnchorManager.updateAnchor(cloudId, newPosition);
  }

  /**
   * Removes a cloud anchor
   */
  public async removeCloudAnchor(cloudId: string): Promise<void> {
    if (!this.cloudAnchorManager) {
      throw new Error('Cloud anchors are not enabled');
    }
    return this.cloudAnchorManager.removeAnchor(cloudId);
  }

  /**
   * Gets all hosted cloud anchors
   */
  public getHostedCloudAnchors(): CloudAnchor[] {
    if (!this.cloudAnchorManager) {
      return [];
    }
    return this.cloudAnchorManager.getHostedAnchors();
  }

  /**
   * Gets all resolved cloud anchors
   */
  public getResolvedCloudAnchors(): CloudAnchor[] {
    if (!this.cloudAnchorManager) {
      return [];
    }
    return this.cloudAnchorManager.getResolvedAnchors();
  }

  /**
   * Stops the AR session and cleans up resources.
   */
  public stop(): void {
    if (this.state === ARSessionState.STOPPED || this.state === ARSessionState.UNINITIALIZED) {
      console.warn('ARSession is not running.');
      return;
    }

    // Clean up managers
    if (this.userSessionManager) {
      this.userSessionManager.endSession();
      this.userSessionManager = null;
    }
    this.cloudAnchorManager = null;
    this.state = ARSessionState.STOPPED;
    // Clean up resources: stop camera, remove sensor listeners, etc.
    this.emit('stopped');
    console.log('ARSession stopped.');
  }

  /**
   * Returns the current state of the AR session.
   */
  public getState(): ARSessionState {
    return this.state;
  }
}