import { androidtvDefaultPort, androidtvKnownApps } from './androidtv.constants.js';
import type { IAndroidtvCommand, IAndroidtvConfig, IAndroidtvDeviceInfo, IAndroidtvDeviceState, IAndroidtvSnapshot } from './androidtv.types.js';

export class AndroidtvUnsupportedProtocolError extends Error {
  constructor(commandArg: IAndroidtvCommand) {
    super(`Android TV live ADB control is not implemented in this TypeScript port. Cannot execute ${commandArg.action} without a real ADB protocol client.`);
    this.name = 'AndroidtvUnsupportedProtocolError';
  }
}

export class AndroidtvClient {
  private readonly snapshot?: IAndroidtvSnapshot;

  constructor(private readonly config: IAndroidtvConfig) {
    this.snapshot = config.snapshot ? this.cloneSnapshot(config.snapshot) : undefined;
  }

  public async getSnapshot(): Promise<IAndroidtvSnapshot> {
    return this.normalizeSnapshot(this.snapshot || this.snapshotFromManualConfig());
  }

  public async turnOn(): Promise<void> {
    return this.unsupported({ action: 'turn_on' });
  }

  public async turnOff(): Promise<void> {
    return this.unsupported({ action: 'turn_off' });
  }

  public async mediaPlay(): Promise<void> {
    return this.unsupported({ action: 'media_play' });
  }

  public async mediaPause(): Promise<void> {
    return this.unsupported({ action: 'media_pause' });
  }

  public async mediaPlayPause(): Promise<void> {
    return this.unsupported({ action: 'media_play_pause' });
  }

  public async mediaStop(): Promise<void> {
    return this.unsupported({ action: 'media_stop' });
  }

  public async setVolumeLevel(volumeLevelArg: number): Promise<void> {
    return this.unsupported({ action: 'volume_set', volumeLevel: volumeLevelArg });
  }

  public async stepVolume(volumeStepArg: number): Promise<void> {
    return this.unsupported({ action: 'volume_step', volumeStep: volumeStepArg });
  }

  public async muteVolume(mutedArg: boolean): Promise<void> {
    return this.unsupported({ action: 'volume_mute', muted: mutedArg });
  }

  public async selectSource(sourceArg: string): Promise<void> {
    const snapshot = await this.getSnapshot();
    const app = snapshot.apps.find((appArg) => sourceArg === appArg.id || sourceArg === (appArg.name || androidtvKnownApps[appArg.id]));
    return this.unsupported({ action: 'select_source', source: sourceArg, appId: app?.id });
  }

  public async sendCommand(commandsArg: string[], repeatsArg = 1): Promise<void> {
    return this.unsupported({ action: 'remote_send_command', keys: commandsArg, repeats: repeatsArg });
  }

  public async adbCommand(commandArg: string): Promise<void> {
    return this.unsupported({ action: 'adb_command', shell: commandArg });
  }

  public async destroy(): Promise<void> {}

  private async unsupported(commandArg: IAndroidtvCommand): Promise<never> {
    throw new AndroidtvUnsupportedProtocolError(commandArg);
  }

  private snapshotFromManualConfig(): IAndroidtvSnapshot {
    const deviceInfo: IAndroidtvDeviceInfo = {
      ...this.config.deviceInfo,
      host: this.config.deviceInfo?.host || this.config.host,
      port: this.config.deviceInfo?.port || this.config.port || androidtvDefaultPort,
      name: this.config.deviceInfo?.name || this.config.deviceName || this.config.host || 'Android TV',
      model: this.config.deviceInfo?.model || this.config.model,
      manufacturer: this.config.deviceInfo?.manufacturer || this.config.manufacturer,
      deviceClass: this.config.deviceInfo?.deviceClass || this.config.deviceClass || 'androidtv',
    };
    const state: IAndroidtvDeviceState = {
      rawState: 'unknown',
      available: false,
      ...this.config.state,
    };
    return {
      deviceInfo,
      state,
      apps: [...(this.config.apps || [])],
    };
  }

  private normalizeSnapshot(snapshotArg: IAndroidtvSnapshot): IAndroidtvSnapshot {
    const deviceInfo: IAndroidtvDeviceInfo = {
      ...snapshotArg.deviceInfo,
      host: snapshotArg.deviceInfo.host || this.config.host,
      port: snapshotArg.deviceInfo.port || this.config.port || androidtvDefaultPort,
      deviceClass: snapshotArg.deviceInfo.deviceClass || this.config.deviceClass || 'androidtv',
    };
    if (!deviceInfo.name) {
      deviceInfo.name = this.config.deviceName || deviceInfo.host || 'Android TV';
    }
    const apps = snapshotArg.apps.map((appArg) => ({
      ...appArg,
      name: appArg.name || androidtvKnownApps[appArg.id],
    }));
    const state = { ...snapshotArg.state };
    if (!state.currentAppName && state.currentAppId) {
      state.currentAppName = apps.find((appArg) => appArg.id === state.currentAppId)?.name || androidtvKnownApps[state.currentAppId];
    }
    if (state.available === undefined) {
      state.available = state.rawState !== 'unknown';
    }
    return {
      deviceInfo,
      state,
      apps,
      updatedAt: snapshotArg.updatedAt,
    };
  }

  private cloneSnapshot(snapshotArg: IAndroidtvSnapshot): IAndroidtvSnapshot {
    return {
      deviceInfo: { ...snapshotArg.deviceInfo },
      state: {
        ...snapshotArg.state,
        runningAppIds: snapshotArg.state.runningAppIds ? [...snapshotArg.state.runningAppIds] : undefined,
      },
      apps: snapshotArg.apps.map((appArg) => ({ ...appArg })),
      updatedAt: snapshotArg.updatedAt,
    };
  }
}
