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 { FoscamClient } from './foscam.classes.client.js';
import { FoscamConfigFlow } from './foscam.classes.configflow.js';
import { createFoscamDiscoveryDescriptor } from './foscam.discovery.js';
import { FoscamMapper } from './foscam.mapper.js';
import type { IFoscamConfig } from './foscam.types.js';
import { foscamDomain } from './foscam.types.js';

export class FoscamIntegration extends BaseIntegration<IFoscamConfig> {
  public readonly domain = foscamDomain;
  public readonly displayName = 'Foscam';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createFoscamDiscoveryDescriptor();
  public readonly configFlow = new FoscamConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/foscam',
    upstreamDomain: foscamDomain,
    integrationType: 'device',
    iotClass: 'local_polling',
    requirements: ['libpyfoscamcgi==0.0.9'],
    dependencies: [] as string[],
    afterDependencies: [] as string[],
    codeowners: ['@Foscam-wangzhengyu'],
    configFlow: true,
    documentation: 'https://www.home-assistant.io/integrations/foscam',
    nativePort: {
      manualLocalDiscovery: true,
      mdnsMetadataMatching: true,
      ssdpMetadataMatching: true,
      snapshotMapping: true,
      liveHttpCgiCommands: true,
      liveEvents: false,
      rtspProxying: false,
      ffmpegProxying: false,
    },
    localApi: {
      implemented: [
        'Foscam CGIProxy.fcgi local HTTP commands used by libpyfoscamcgi 0.0.9 and the Home Assistant foscam integration',
        'manual/local host config flow with Main/Sub stream and RTSP port options',
        'camera snapshot image retrieval through snapPicture2 and RTSP stream source URL mapping',
        'PTZ movement, PTZ preset, motion detection, upstream switch commands, and volume number commands where exposed by the camera',
      ],
      explicitUnsupported: [
        'live video proxying, RTSP proxying, and ffmpeg transcoding',
        'fake live event or command success without a configured HTTP endpoint, injected client, or command executor',
      ],
    },
  };

  public async setup(configArg: IFoscamConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new FoscamRuntime(new FoscamClient(configArg));
  }

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

export class HomeAssistantFoscamIntegration extends FoscamIntegration {}

class FoscamRuntime implements IIntegrationRuntime {
  public domain = foscamDomain;

  constructor(private readonly client: FoscamClient) {}

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

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

  public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void): Promise<() => Promise<void>> {
    void handlerArg;
    throw new Error('Foscam live event subscription is not implemented in this TypeScript port; use polling or explicit refreshes.');
  }

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === foscamDomain && requestArg.service === 'refresh') {
        const result = await this.client.refresh();
        return { success: result.success, error: result.error, data: result.snapshot || result.data };
      }

      const snapshot = await this.client.getSnapshot();
      const command = FoscamMapper.commandForService(snapshot, requestArg);
      if (!command) {
        return { success: false, error: `Unsupported Foscam service: ${requestArg.domain}.${requestArg.service}` };
      }
      const data = await this.client.execute(command);
      const ok = data && typeof data === 'object' && 'ok' in data ? Boolean((data as { ok?: unknown }).ok) : true;
      return { success: ok, data };
    } catch (errorArg) {
      return { success: false, error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
    }
  }

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