import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { BaseIntegration } from '../../core/classes.baseintegration.js';
import type { IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
import { DlinkClient } from './dlink.classes.client.js';
import { DlinkConfigFlow } from './dlink.classes.configflow.js';
import { dlinkDefaultName, dlinkDomain } from './dlink.constants.js';
import { createDlinkDiscoveryDescriptor } from './dlink.discovery.js';
import { DlinkMapper } from './dlink.mapper.js';
import type { IDlinkConfig } from './dlink.types.js';

export class DlinkIntegration extends BaseIntegration<IDlinkConfig> {
  public readonly domain = dlinkDomain;
  public readonly displayName = 'D-Link Wi-Fi Smart Plugs';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createDlinkDiscoveryDescriptor();
  public readonly configFlow = new DlinkConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/dlink',
    upstreamDomain: dlinkDomain,
    integrationType: 'device',
    iotClass: 'local_polling',
    requirements: ['pyW215==0.8.0'],
    dependencies: [] as string[],
    afterDependencies: [] as string[],
    codeowners: ['@tkdrob'],
    documentation: 'https://www.home-assistant.io/integrations/dlink',
    configFlow: true,
    discovery: {
      dhcp: ['hostname:dsp-w215'],
      manual: true,
      note: 'Home Assistant discovers dlink by DHCP hostname dsp-w215. This native port recognizes DHCP and manual host/snapshot candidates.',
    },
    runtime: {
      type: 'control-runtime',
      polling: 'local HNAP HTTP or supplied snapshot/nativeClient',
      platforms: ['switch'],
      services: ['switch.turn_on', 'switch.turn_off', 'dlink.set_state', 'dlink.refresh', 'dlink.snapshot'],
    },
    localApi: {
      implemented: [
        'DHCP hostname discovery for dsp-w215 and manual host/snapshot discovery',
        'Config flow shape for host, username, password, legacy protocol, native HTTP, port, timeout, and snapshot JSON',
        'Native HNAP SOAP authentication, switch state reads, switch commands, temperature, current consumption, and total consumption where firmware exposes them',
        'Legacy my_cgi.cgi current-consumption read path used by pyW215',
        'Snapshot-to-device/entity mapper for the Home Assistant smart plug switch representation',
      ],
      explicitUnsupported: [
        'D-Link cloud APIs',
        'DSP-W218 and newer devices using a different protocol than pyW215',
        'Claiming live command success without host/password native HTTP, nativeClient.setState, commandExecutor, or transport.execute',
      ],
    },
  };

  public async setup(configArg: IDlinkConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new DlinkRuntime(new DlinkClient(configArg));
  }

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

export class HomeAssistantDlinkIntegration extends DlinkIntegration {}

class DlinkRuntime implements IIntegrationRuntime {
  public domain = dlinkDomain;

  constructor(private readonly client: DlinkClient) {}

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

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

  public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void): Promise<() => Promise<void>> {
    const unsubscribe = this.client.onEvent((eventArg) => handlerArg(DlinkMapper.toIntegrationEvent(eventArg)));
    return async () => unsubscribe();
  }

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.domain === dlinkDomain && (requestArg.service === 'snapshot' || requestArg.service === 'status')) {
      return { success: true, data: await this.client.getSnapshot() };
    }
    if (requestArg.domain === dlinkDomain && requestArg.service === 'refresh') {
      return this.client.refresh();
    }
    const snapshot = await this.client.getSnapshot();
    const command = DlinkMapper.commandForService(snapshot, requestArg);
    if (!command) {
      return { success: false, error: `Unsupported D-Link service or target: ${requestArg.domain}.${requestArg.service}` };
    }
    return this.client.sendCommand(command);
  }

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