import * as plugins from '../../plugins.js';
import { BaseIntegration } from '../../core/classes.baseintegration.js';
import type { IIntegrationEntity, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
import { HarmonyClient } from './harmony.classes.client.js';
import { HarmonyConfigFlow } from './harmony.classes.configflow.js';
import {
  harmonyDefaultDelaySecs,
  harmonyDefaultHoldSecs,
  harmonyDefaultRepeats,
  harmonyDisplayName,
  harmonyDomain,
  harmonyHubPort,
  harmonyPowerOffActivity,
  harmonyPreviousActiveActivity,
  harmonySelectPowerOffOption,
  harmonySsdpDeviceType,
} from './harmony.constants.js';
import { createHarmonyDiscoveryDescriptor } from './harmony.discovery.js';
import { HarmonyMapper } from './harmony.mapper.js';
import type { IHarmonyConfig } from './harmony.types.js';

export class HarmonyIntegration extends BaseIntegration<IHarmonyConfig> {
  public readonly domain = harmonyDomain;
  public readonly displayName = harmonyDisplayName;
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createHarmonyDiscoveryDescriptor();
  public readonly configFlow = new HarmonyConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/harmony',
    upstreamDomain: harmonyDomain,
    integrationType: 'device',
    iotClass: 'local_push',
    requirements: ['aioharmony==0.5.3'],
    dependencies: ['remote'],
    afterDependencies: [],
    codeowners: ['@ehendrix23', '@bdraco', '@mkeesey', '@Aohzan'],
    configFlow: true,
    documentation: 'https://www.home-assistant.io/integrations/harmony',
    ssdp: [{ deviceType: harmonySsdpDeviceType, manufacturer: 'Logitech' }],
    runtime: {
      type: 'control-runtime',
      polling: 'snapshot or injected local Harmony client/executor',
      services: ['snapshot', 'remote.turn_on', 'remote.turn_off', 'remote.send_command', 'harmony.change_channel', 'select.select_option'],
      controls: true,
    },
    localApi: {
      implemented: [
        'SSDP Harmony Hub discovery by deviceType/manufacturer',
        'manual host setup',
        'Harmony Hub snapshot normalization from native config or upstream hub config shape',
        'activity, select, media_player, device and remote-command modeling',
        'command dispatch through injected client/executor',
      ],
      explicitUnsupported: [
        'Harmony cloud/account APIs',
        'cloud-backed sync service',
        'reporting command success without an injected client/executor or host-provided local transport',
      ],
    },
  };

  public async setup(configArg: IHarmonyConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new HarmonyRuntime(new HarmonyClient(configArg));
  }

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

export class HomeAssistantHarmonyIntegration extends HarmonyIntegration {}

class HarmonyRuntime implements IIntegrationRuntime {
  public domain = harmonyDomain;

  constructor(private readonly client: HarmonyClient) {}

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

  public async entities(): Promise<IIntegrationEntity[]> {
    return HarmonyMapper.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 === 'media_player') {
        return await this.callMediaPlayerService(requestArg);
      }
      if (requestArg.domain === 'select') {
        return await this.callSelectService(requestArg);
      }
      if (requestArg.domain === harmonyDomain) {
        return await this.callHarmonyService(requestArg);
      }
      return { success: false, error: `Unsupported Harmony Hub 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 callRemoteService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'turn_on') {
      const activity = await this.activityForTurnOn(requestArg);
      if (!activity) {
        return { success: false, error: 'Harmony Hub remote.turn_on requires data.activity, a default activity, a previous active activity, or at least one configured activity.' };
      }
      await this.client.startActivity(activity, 'remote_turn_on');
      return { success: true };
    }
    if (requestArg.service === 'turn_off') {
      await this.client.powerOff('remote_turn_off');
      return { success: true };
    }
    if (requestArg.service !== 'send_command') {
      return { success: false, error: `Unsupported Harmony Hub remote service: ${requestArg.service}` };
    }
    const device = this.stringData(requestArg, 'device');
    if (!device) {
      return { success: false, error: 'Harmony Hub remote.send_command requires data.device.' };
    }
    const commands = this.stringArrayData(requestArg, 'command');
    if (!commands?.length) {
      return { success: false, error: 'Harmony Hub remote.send_command requires data.command.' };
    }
    await this.client.sendCommands(commands, device, {
      numRepeats: this.numberData(requestArg, 'num_repeats') ?? this.numberData(requestArg, 'numRepeats') ?? harmonyDefaultRepeats,
      delaySecs: this.numberData(requestArg, 'delay_secs') ?? this.numberData(requestArg, 'delaySecs') ?? this.client.delaySecs,
      holdSecs: this.numberData(requestArg, 'hold_secs') ?? this.numberData(requestArg, 'holdSecs') ?? this.client.holdSecs,
      reason: 'remote_send_command',
    });
    return { success: true };
  }

  private async callMediaPlayerService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'turn_on') {
      return this.callRemoteService({ ...requestArg, domain: 'remote', service: 'turn_on' });
    }
    if (requestArg.service === 'turn_off') {
      return this.callRemoteService({ ...requestArg, domain: 'remote', service: 'turn_off' });
    }
    if (requestArg.service === 'select_source') {
      const source = this.stringData(requestArg, 'source');
      if (!source) {
        return { success: false, error: 'Harmony Hub media_player.select_source requires data.source.' };
      }
      await this.client.startActivity(source, 'select_activity');
      return { success: true };
    }
    if (requestArg.service === 'play_media') {
      const mediaType = this.stringData(requestArg, 'media_content_type') || this.stringData(requestArg, 'media_type');
      const mediaId = this.stringData(requestArg, 'media_content_id') || this.stringData(requestArg, 'media_id') || this.stringData(requestArg, 'channel');
      if (mediaType !== 'channel') {
        return { success: false, error: 'Harmony Hub media_player.play_media only supports media_content_type channel.' };
      }
      const channel = this.channelValue(mediaId);
      if (!channel) {
        return { success: false, error: 'Harmony Hub media_player.play_media requires a positive integer channel media_content_id.' };
      }
      await this.client.changeChannel(channel, 'change_channel');
      return { success: true };
    }
    return { success: false, error: `Unsupported Harmony Hub media_player service: ${requestArg.service}` };
  }

  private async callSelectService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service !== 'select_option') {
      return { success: false, error: `Unsupported Harmony Hub select service: ${requestArg.service}` };
    }
    const option = this.stringData(requestArg, 'option');
    if (!option) {
      return { success: false, error: 'Harmony Hub select.select_option requires data.option.' };
    }
    if (option === harmonySelectPowerOffOption || option === harmonyPowerOffActivity) {
      await this.client.powerOff('select_option');
      return { success: true };
    }
    await this.client.startActivity(option, 'select_option');
    return { success: true };
  }

  private async callHarmonyService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'snapshot' || requestArg.service === 'status') {
      return { success: true, data: await this.client.getSnapshot() };
    }
    if (requestArg.service === 'connect') {
      await this.client.connect();
      return { success: true };
    }
    if (requestArg.service === 'start_activity' || requestArg.service === 'turn_on') {
      const activity = this.stringData(requestArg, 'activity') || this.stringData(requestArg, 'name') || this.stringData(requestArg, 'activity_id');
      if (!activity) {
        return { success: false, error: `Harmony Hub ${requestArg.service} requires data.activity.` };
      }
      await this.client.startActivity(activity, 'select_activity');
      return { success: true };
    }
    if (requestArg.service === 'change_channel') {
      const channel = this.channelValue(requestArg.data?.channel);
      if (!channel) {
        return { success: false, error: 'Harmony Hub change_channel requires data.channel as a positive integer.' };
      }
      await this.client.changeChannel(channel, 'change_channel');
      return { success: true };
    }
    if (requestArg.service === 'send_command') {
      return this.callRemoteService({ ...requestArg, domain: 'remote', service: 'send_command' });
    }
    if (requestArg.service === 'sync') {
      await this.client.sync();
      return { success: true };
    }
    return { success: false, error: `Unsupported Harmony Hub service: ${requestArg.service}` };
  }

  private async activityForTurnOn(requestArg: IServiceCallRequest): Promise<string | undefined> {
    const requested = this.stringData(requestArg, 'activity');
    if (requested && requested !== harmonyPreviousActiveActivity) {
      return requested;
    }
    const explicitlyRequestedPrevious = requested === harmonyPreviousActiveActivity;
    if (!explicitlyRequestedPrevious && this.client.defaultActivity && this.client.defaultActivity !== harmonyPreviousActiveActivity) {
      return this.client.defaultActivity;
    }
    const snapshot = await this.client.getSnapshot();
    if (snapshot.state.lastActivity) {
      return snapshot.state.lastActivity;
    }
    return snapshot.activities.find((activityArg) => !activityArg.isPowerOff && activityArg.label !== harmonyPowerOffActivity)?.label;
  }

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

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

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

  private channelValue(valueArg: unknown): number | undefined {
    if (typeof valueArg === 'number' && Number.isInteger(valueArg) && valueArg > 0) {
      return valueArg;
    }
    if (typeof valueArg === 'string' && /^\d+$/.test(valueArg)) {
      const channel = Number(valueArg);
      return channel > 0 ? channel : undefined;
    }
    return undefined;
  }
}
