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 { SamsungtvClient } from './samsungtv.classes.client.js';
import { SamsungtvConfigFlow } from './samsungtv.classes.configflow.js';
import { createSamsungtvDiscoveryDescriptor } from './samsungtv.discovery.js';
import { SamsungtvMapper } from './samsungtv.mapper.js';
import type { ISamsungtvConfig } from './samsungtv.types.js';

export class SamsungtvIntegration extends BaseIntegration<ISamsungtvConfig> {
  public readonly domain = 'samsungtv';
  public readonly displayName = 'Samsung Smart TV';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createSamsungtvDiscoveryDescriptor();
  public readonly configFlow = new SamsungtvConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/samsungtv',
    upstreamDomain: 'samsungtv',
    integrationType: 'device',
    iotClass: 'local_push',
    qualityScale: 'gold',
    requirements: [
      'getmac==0.9.5',
      'samsungctl[websocket]==0.7.1',
      'samsungtvws[async,encrypted]==2.7.2',
      'wakeonlan==3.1.0',
      'async-upnp-client==0.46.2',
    ],
    dependencies: ['ssdp'],
    codeowners: ['@chemelli74', '@epenet'],
  };

  public async setup(configArg: ISamsungtvConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new SamsungtvRuntime(new SamsungtvClient(configArg));
  }

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

export class HomeAssistantSamsungtvIntegration extends SamsungtvIntegration {}

class SamsungtvRuntime implements IIntegrationRuntime {
  public domain = 'samsungtv';

  constructor(private readonly client: SamsungtvClient) {}

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

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

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      return await this.callServiceUnsafe(requestArg);
    } catch (error) {
      return { success: false, error: this.errorMessage(error) };
    }
  }

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

  private async callServiceUnsafe(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.domain === 'remote') {
      return this.callRemoteService(requestArg);
    }

    if (requestArg.domain !== 'media_player') {
      return { success: false, error: `Unsupported Samsung TV service domain: ${requestArg.domain}` };
    }

    if (requestArg.service === 'turn_on') {
      await this.client.turnOn();
      return { success: true };
    }
    if (requestArg.service === 'turn_off') {
      await this.client.turnOff();
      return { success: true };
    }
    if (requestArg.service === 'media_play' || requestArg.service === 'play') {
      await this.client.play();
      return { success: true };
    }
    if (requestArg.service === 'media_pause' || requestArg.service === 'pause') {
      await this.client.pause();
      return { success: true };
    }
    if (requestArg.service === 'media_play_pause' || requestArg.service === 'play_pause') {
      await this.client.playPause();
      return { success: true };
    }
    if (requestArg.service === 'media_stop' || requestArg.service === 'stop') {
      await this.client.sendKeys(['KEY_STOP']);
      return { success: true };
    }
    if (requestArg.service === 'media_next_track' || requestArg.service === 'next_track') {
      await this.client.sendKeys(['KEY_CHUP']);
      return { success: true };
    }
    if (requestArg.service === 'media_previous_track' || requestArg.service === 'previous_track') {
      await this.client.sendKeys(['KEY_CHDOWN']);
      return { success: true };
    }
    if (requestArg.service === 'volume_up') {
      await this.client.sendKeys(['KEY_VOLUP']);
      return { success: true };
    }
    if (requestArg.service === 'volume_down') {
      await this.client.sendKeys(['KEY_VOLDOWN']);
      return { success: true };
    }
    if (requestArg.service === 'volume_mute' || requestArg.service === 'mute') {
      await this.client.sendKeys(['KEY_MUTE']);
      return { success: true };
    }
    if (requestArg.service === 'volume_set') {
      const volumeLevel = requestArg.data?.volume_level;
      if (typeof volumeLevel !== 'number') {
        return { success: false, error: 'Samsung TV volume_set requires data.volume_level.' };
      }
      await this.client.setVolumeLevel(volumeLevel);
      return { success: true };
    }
    if (requestArg.service === 'select_source') {
      const source = requestArg.data?.source;
      if (typeof source !== 'string' || !source) {
        return { success: false, error: 'Samsung TV select_source requires data.source.' };
      }
      await this.client.selectSource(source);
      return { success: true };
    }

    return { success: false, error: `Unsupported Samsung TV media_player service: ${requestArg.service}` };
  }

  private async callRemoteService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service !== 'send_command') {
      return { success: false, error: `Unsupported Samsung TV remote service: ${requestArg.service}` };
    }

    const command = requestArg.data?.command;
    const commands = Array.isArray(command) ? command : [command];
    const keys = commands.filter((commandArg): commandArg is string => typeof commandArg === 'string' && Boolean(commandArg));
    if (!keys.length) {
      return { success: false, error: 'Samsung TV 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;
    for (let index = 0; index < repeats; index += 1) {
      await this.client.sendKeys(keys);
    }
    return { success: true };
  }

  private errorMessage(errorArg: unknown): string {
    if (errorArg instanceof Error) {
      return errorArg.message;
    }
    return String(errorArg);
  }
}
