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 { DenonClient } from './denon.classes.client.js';
import { DenonConfigFlow } from './denon.classes.configflow.js';
import { createDenonDiscoveryDescriptor } from './denon.discovery.js';
import { DenonMapper } from './denon.mapper.js';
import type { IDenonCommandRequest, IDenonConfig, TDenonCommand } from './denon.types.js';
import { denonDomain } from './denon.types.js';

export class DenonIntegration extends BaseIntegration<IDenonConfig> {
  public readonly domain = denonDomain;
  public readonly displayName = 'Denon Network Receivers';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createDenonDiscoveryDescriptor();
  public readonly configFlow = new DenonConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/denon',
    upstreamDomain: denonDomain,
    integrationType: 'device',
    iotClass: 'local_polling',
    qualityScale: 'legacy',
    requirements: [],
    dependencies: [],
    afterDependencies: [],
    codeowners: [],
    configFlow: true,
    runtime: {
      type: 'control-runtime',
      polling: 'local Denon telnet protocol',
      services: ['snapshot', 'refresh', 'media_player controls'],
      controls: true,
    },
    localApi: {
      implemented: [
        'manual host setup',
        'telnet snapshot polling via PW?, MV?, MU?, SI?, NSFRN?, SSFUN?, SSSOD?, and NSE',
        'power, volume, mute, source selection, and media transport telnet commands',
      ],
      explicitUnsupported: [
        'claiming live command success without config.host, injected client.execute, or commandExecutor',
      ],
    },
  };

  public async setup(configArg: IDenonConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new DenonRuntime(new DenonClient(configArg));
  }

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

export class HomeAssistantDenonIntegration extends DenonIntegration {}

class DenonRuntime implements IIntegrationRuntime {
  public domain = denonDomain;

  constructor(private readonly client: DenonClient) {}

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

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

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === denonDomain) {
        return this.callDenonService(requestArg);
      }
      if (requestArg.domain !== 'media_player') {
        return { success: false, error: `Unsupported Denon service domain: ${requestArg.domain}` };
      }
      const command = this.commandFromService(requestArg);
      if (!command) {
        return { success: false, error: `Unsupported Denon media_player service: ${requestArg.service}` };
      }
      const request: IDenonCommandRequest = {
        command,
        source: this.stringData(requestArg, 'source'),
        volumeLevel: this.numberData(requestArg, 'volume_level'),
        muted: this.boolData(requestArg, 'is_volume_muted') ?? this.boolData(requestArg, 'mute'),
      };
      return { success: true, data: await this.client.execute(request) };
    } catch (errorArg) {
      return { success: false, error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
    }
  }

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

  private async callDenonService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'snapshot' || requestArg.service === 'status') {
      return { success: true, data: await this.client.getSnapshot() };
    }
    if (requestArg.service === 'refresh' || requestArg.service === 'reload') {
      const snapshot = await this.client.refresh();
      return snapshot.online
        ? { success: true, data: snapshot }
        : { success: false, error: snapshot.error || 'Denon refresh requires config.host, config.snapshot, injected client, or commandExecutor.', data: snapshot };
    }
    if (requestArg.service === 'raw_command') {
      const rawCommand = this.stringData(requestArg, 'command') || this.stringData(requestArg, 'rawCommand');
      if (!rawCommand) {
        return { success: false, error: 'Denon raw_command requires data.command.' };
      }
      return { success: true, data: await this.client.execute({ command: 'raw', rawCommand }) };
    }
    return { success: false, error: `Unsupported Denon service: ${requestArg.service}` };
  }

  private commandFromService(requestArg: IServiceCallRequest): TDenonCommand | undefined {
    if (requestArg.service === 'turn_on') {
      return 'turn_on';
    }
    if (requestArg.service === 'turn_off') {
      return 'turn_off';
    }
    if (requestArg.service === 'volume_up') {
      return 'volume_up';
    }
    if (requestArg.service === 'volume_down') {
      return 'volume_down';
    }
    if (requestArg.service === 'volume_set') {
      return 'set_volume';
    }
    if (requestArg.service === 'volume_mute') {
      return 'mute';
    }
    if (requestArg.service === 'select_source') {
      return 'select_source';
    }
    if (requestArg.service === 'media_play' || requestArg.service === 'play') {
      return 'play';
    }
    if (requestArg.service === 'media_pause' || requestArg.service === 'pause') {
      return 'pause';
    }
    if (requestArg.service === 'media_stop' || requestArg.service === 'stop') {
      return 'stop';
    }
    if (requestArg.service === 'media_next_track' || requestArg.service === 'next_track') {
      return 'next_track';
    }
    if (requestArg.service === 'media_previous_track' || requestArg.service === 'previous_track') {
      return 'previous_track';
    }
    return undefined;
  }

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

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

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