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 { FrontierSiliconClient } from './frontier_silicon.classes.client.js';
import { FrontierSiliconConfigFlow } from './frontier_silicon.classes.configflow.js';
import { createFrontierSiliconDiscoveryDescriptor } from './frontier_silicon.discovery.js';
import { FrontierSiliconMapper } from './frontier_silicon.mapper.js';
import type { IFrontierSiliconCommandRequest, IFrontierSiliconConfig, IFrontierSiliconSnapshot, TFrontierSiliconCommand, TFrontierSiliconRepeatMode } from './frontier_silicon.types.js';

export class FrontierSiliconIntegration extends BaseIntegration<IFrontierSiliconConfig> {
  public readonly domain = 'frontier_silicon';
  public readonly displayName = 'Frontier Silicon';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createFrontierSiliconDiscoveryDescriptor();
  public readonly configFlow = new FrontierSiliconConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/frontier_silicon',
    upstreamDomain: 'frontier_silicon',
    integrationType: 'device',
    iotClass: 'local_polling',
    requirements: ['afsapi==1.0.0'],
    dependencies: [],
    afterDependencies: [],
    codeowners: ['@wlcrs'],
    configFlow: true,
    documentation: 'https://www.home-assistant.io/integrations/frontier_silicon',
    ssdp: [{ st: 'urn:schemas-frontier-silicon-com:undok:fsapi:1' }],
  };

  public async setup(configArg: IFrontierSiliconConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new FrontierSiliconRuntime(new FrontierSiliconClient(configArg));
  }

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

export class HomeAssistantFrontierSiliconIntegration extends FrontierSiliconIntegration {}

class FrontierSiliconRuntime implements IIntegrationRuntime {
  public domain = 'frontier_silicon';

  constructor(private readonly client: FrontierSiliconClient) {}

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

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

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === 'media_player') {
        return await this.callMediaPlayerService(requestArg);
      }
      if (requestArg.domain === 'frontier_silicon') {
        return await this.callFrontierSiliconService(requestArg);
      }
      return { success: false, error: `Unsupported Frontier Silicon service domain: ${requestArg.domain}` };
    } 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> {
    if (requestArg.service === 'turn_on') {
      return { success: true, data: await this.client.execute({ command: 'set_power', powered: true }) };
    }
    if (requestArg.service === 'turn_off') {
      return { success: true, data: await this.client.execute({ command: 'set_power', powered: false }) };
    }
    const command = this.commandFromMediaService(requestArg.service);
    if (!command) {
      return { success: false, error: `Unsupported Frontier Silicon media_player service: ${requestArg.service}` };
    }
    return { success: true, data: await this.client.execute(this.commandRequest(command, requestArg)) };
  }

  private async callFrontierSiliconService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'snapshot') {
      return { success: true, data: await this.client.snapshot() };
    }
    if (requestArg.service === 'restore') {
      await this.client.restore(requestArg.data?.snapshot as IFrontierSiliconSnapshot | undefined);
      return { success: true };
    }
    if (requestArg.service === 'play_preset') {
      return { success: true, data: await this.client.execute({ command: 'play_preset', preset: this.numberData(requestArg, 'preset') ?? this.numberData(requestArg, 'preset_number'), presetIndex: this.numberData(requestArg, 'preset_index') }) };
    }
    if (requestArg.service === 'command' || requestArg.service === 'raw_command') {
      return { success: true, data: await this.client.execute({ command: 'raw_command', path: this.stringData(requestArg, 'path') || this.stringData(requestArg, 'command'), node: this.stringData(requestArg, 'node'), value: this.primitiveData(requestArg, 'value'), params: this.paramsData(requestArg) }) };
    }
    if (requestArg.service === 'turn_on' || requestArg.service === 'turn_off' || this.commandFromMediaService(requestArg.service)) {
      return this.callMediaPlayerService({ ...requestArg, domain: 'media_player' });
    }
    return { success: false, error: `Unsupported Frontier Silicon service: ${requestArg.service}` };
  }

  private commandFromMediaService(serviceArg: string): TFrontierSiliconCommand | undefined {
    if (serviceArg === 'media_play' || serviceArg === 'play') {
      return 'play';
    }
    if (serviceArg === 'media_pause' || serviceArg === 'pause') {
      return 'pause';
    }
    if (serviceArg === 'media_stop' || serviceArg === 'stop') {
      return 'stop';
    }
    if (serviceArg === 'media_next_track' || serviceArg === 'next_track' || serviceArg === 'next') {
      return 'next_track';
    }
    if (serviceArg === 'media_previous_track' || serviceArg === 'previous_track' || serviceArg === 'previous') {
      return 'previous_track';
    }
    if (serviceArg === 'volume_set' || serviceArg === 'set_volume') {
      return 'set_volume';
    }
    if (serviceArg === 'volume_up') {
      return 'volume_up';
    }
    if (serviceArg === 'volume_down') {
      return 'volume_down';
    }
    if (serviceArg === 'volume_mute' || serviceArg === 'mute') {
      return 'mute';
    }
    if (serviceArg === 'select_source' || serviceArg === 'source') {
      return 'select_source';
    }
    if (serviceArg === 'select_sound_mode' || serviceArg === 'set_sound_mode') {
      return 'set_sound_mode';
    }
    if (serviceArg === 'repeat_set' || serviceArg === 'set_repeat') {
      return 'set_repeat';
    }
    if (serviceArg === 'shuffle_set' || serviceArg === 'set_shuffle') {
      return 'set_shuffle';
    }
    if (serviceArg === 'media_seek' || serviceArg === 'seek') {
      return 'seek';
    }
    if (serviceArg === 'play_media') {
      return 'play_media';
    }
    return undefined;
  }

  private commandRequest(commandArg: TFrontierSiliconCommand, requestArg: IServiceCallRequest): IFrontierSiliconCommandRequest {
    const base: IFrontierSiliconCommandRequest = { command: commandArg };
    if (commandArg === 'set_volume') {
      base.volumeLevel = this.numberData(requestArg, 'volume_level');
      base.volume = this.numberData(requestArg, 'volume');
    }
    if (commandArg === 'mute') {
      base.muted = this.booleanData(requestArg, 'is_volume_muted') ?? this.booleanData(requestArg, 'muted') ?? this.booleanData(requestArg, 'mute');
    }
    if (commandArg === 'select_source') {
      base.source = this.stringData(requestArg, 'source');
    }
    if (commandArg === 'set_sound_mode') {
      base.soundMode = this.stringData(requestArg, 'sound_mode') || this.stringData(requestArg, 'soundMode');
    }
    if (commandArg === 'set_repeat') {
      const repeat = this.stringData(requestArg, 'repeat') || this.stringData(requestArg, 'repeat_mode');
      if (repeat === 'off' || repeat === 'all' || repeat === 'one') {
        base.repeat = repeat as TFrontierSiliconRepeatMode;
      }
    }
    if (commandArg === 'set_shuffle') {
      base.shuffle = this.booleanData(requestArg, 'shuffle');
    }
    if (commandArg === 'seek') {
      base.position = this.numberData(requestArg, 'seek_position') ?? this.numberData(requestArg, 'position');
    }
    if (commandArg === 'play_media') {
      base.mediaId = this.stringData(requestArg, 'media_content_id') || this.stringData(requestArg, 'mediaId');
      base.mediaType = this.stringData(requestArg, 'media_content_type') || this.stringData(requestArg, 'mediaType');
    }
    return base;
  }

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

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

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

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

  private paramsData(requestArg: IServiceCallRequest): Record<string, string | number | boolean> | undefined {
    const params = requestArg.data?.params;
    if (!params || typeof params !== 'object' || Array.isArray(params)) {
      return undefined;
    }
    const result: Record<string, string | number | boolean> = {};
    for (const [key, value] of Object.entries(params)) {
      if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
        result[key] = value;
      }
    }
    return result;
  }
}
