import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { BaseIntegration } from '../../core/classes.baseintegration.js';
import type { IIntegrationEntity, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
import { DunehdClient } from './dunehd.classes.client.js';
import { DunehdConfigFlow } from './dunehd.classes.configflow.js';
import { createDunehdDiscoveryDescriptor } from './dunehd.discovery.js';
import { DunehdMapper } from './dunehd.mapper.js';
import type { IDunehdCommandRequest, IDunehdConfig, TDunehdPlaybackCommand } from './dunehd.types.js';

export class DunehdIntegration extends BaseIntegration<IDunehdConfig> {
  public readonly domain = 'dunehd';
  public readonly displayName = 'Dune HD';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createDunehdDiscoveryDescriptor();
  public readonly configFlow = new DunehdConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/dunehd',
    upstreamDomain: 'dunehd',
    integrationType: 'device',
    iotClass: 'local_polling',
    requirements: ['pdunehd==1.3.2'],
    dependencies: [],
    afterDependencies: [],
    codeowners: [],
    configFlow: true,
    documentation: 'https://www.home-assistant.io/integrations/dunehd',
  };

  public async setup(configArg: IDunehdConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new DunehdRuntime(new DunehdClient(configArg));
  }

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

export class HomeAssistantDunehdIntegration extends DunehdIntegration {}

class DunehdRuntime implements IIntegrationRuntime {
  public domain = 'dunehd';

  constructor(private readonly client: DunehdClient) {}

  public async devices(): Promise<shxInterfaces.data.IDeviceDefinition[]> {
    return DunehdMapper.toDevices(await this.client.getSnapshot());
  }

  public async entities(): Promise<IIntegrationEntity[]> {
    return DunehdMapper.toEntities(await this.client.getSnapshot());
  }

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === 'remote') {
        return await this.callRemoteService(requestArg);
      }
      if (requestArg.domain === 'dunehd') {
        return await this.callDunehdService(requestArg);
      }
      if (requestArg.domain !== 'media_player') {
        return { success: false, error: `Unsupported Dune HD service domain: ${requestArg.domain}` };
      }
      return await this.callMediaPlayerService(requestArg);
    } catch (errorArg) {
      return { success: false, error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
    }
  }

  public async destroy(): Promise<void> {
    await this.client.destroy();
  }

  private async callMediaPlayerService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    const command = this.commandFromMediaService(requestArg.service);
    if (!command) {
      return { success: false, error: `Unsupported Dune HD media_player service: ${requestArg.service}` };
    }
    const result = await this.client.execute(this.commandRequest(command, requestArg));
    return { success: true, data: result };
  }

  private async callRemoteService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service !== 'send_command') {
      return { success: false, error: `Unsupported Dune HD remote service: ${requestArg.service}` };
    }
    const commands = this.stringArrayData(requestArg, 'command');
    if (!commands?.length) {
      return { success: false, error: 'Dune HD remote.send_command requires data.command.' };
    }
    const repeatsValue = requestArg.data?.num_repeats ?? requestArg.data?.numRepeats ?? 1;
    const repeats = typeof repeatsValue === 'number' && Number.isFinite(repeatsValue) ? Math.max(1, Math.floor(repeatsValue)) : 1;
    let result: unknown;
    for (let index = 0; index < repeats; index += 1) {
      for (const command of commands) {
        result = await this.client.execute({ command: 'remote', remoteCommand: command });
      }
    }
    return { success: true, data: result };
  }

  private async callDunehdService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'send_ir_code') {
      const code = this.stringData(requestArg, 'ir_code') || this.stringData(requestArg, 'code');
      if (!code) {
        return { success: false, error: 'Dune HD send_ir_code requires data.ir_code.' };
      }
      return { success: true, data: await this.client.execute({ command: 'remote', remoteCommand: code }) };
    }
    if (requestArg.service === 'status') {
      return { success: true, data: await this.client.updateState() };
    }
    return { success: false, error: `Unsupported Dune HD service: ${requestArg.service}` };
  }

  private commandFromMediaService(serviceArg: string): TDunehdPlaybackCommand | undefined {
    if (serviceArg === 'turn_on') {
      return 'turn_on';
    }
    if (serviceArg === 'turn_off') {
      return 'turn_off';
    }
    if (serviceArg === 'play' || serviceArg === 'media_play') {
      return 'play';
    }
    if (serviceArg === 'pause' || serviceArg === 'media_pause') {
      return 'pause';
    }
    if (serviceArg === 'stop' || serviceArg === 'media_stop') {
      return 'stop';
    }
    if (serviceArg === 'previous_track' || serviceArg === 'media_previous_track') {
      return 'previous_track';
    }
    if (serviceArg === 'next_track' || serviceArg === 'media_next_track') {
      return 'next_track';
    }
    if (serviceArg === 'volume_up') {
      return 'volume_up';
    }
    if (serviceArg === 'volume_down') {
      return 'volume_down';
    }
    if (serviceArg === 'volume_mute' || serviceArg === 'mute') {
      return 'mute';
    }
    if (serviceArg === 'play_media') {
      return 'play_media';
    }
    return undefined;
  }

  private commandRequest(commandArg: TDunehdPlaybackCommand, requestArg: IServiceCallRequest): IDunehdCommandRequest {
    const result: IDunehdCommandRequest = { command: commandArg };
    if (commandArg === 'mute') {
      result.muted = this.boolData(requestArg, 'is_volume_muted') ?? this.boolData(requestArg, 'muted') ?? this.boolData(requestArg, 'mute');
    }
    if (commandArg === 'play_media') {
      result.mediaUrl = this.stringData(requestArg, 'media_content_id') || this.stringData(requestArg, 'uri') || this.stringData(requestArg, 'mediaUrl');
    }
    return result;
  }

  private stringData(requestArg: IServiceCallRequest, keyArg: string): string | undefined {
    const value = requestArg.data?.[keyArg];
    return typeof value === 'string' && value ? value : undefined;
  }

  private stringArrayData(requestArg: IServiceCallRequest, keyArg: string): string[] | undefined {
    const value = requestArg.data?.[keyArg];
    if (typeof value === 'string' && value) {
      return [value];
    }
    return Array.isArray(value) && value.every((itemArg) => typeof itemArg === 'string' && Boolean(itemArg)) ? value : undefined;
  }

  private boolData(requestArg: IServiceCallRequest, keyArg: string): boolean | undefined {
    const value = requestArg.data?.[keyArg];
    return typeof value === 'boolean' ? value : undefined;
  }
}
