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 { FritzboxCallmonitorClient } from './fritzbox_callmonitor.classes.client.js';
import { FritzboxCallmonitorConfigFlow } from './fritzbox_callmonitor.classes.configflow.js';
import { createFritzboxCallmonitorDiscoveryDescriptor } from './fritzbox_callmonitor.discovery.js';
import { FritzboxCallmonitorMapper } from './fritzbox_callmonitor.mapper.js';
import type { IFritzboxCallmonitorConfig } from './fritzbox_callmonitor.types.js';
import { fritzboxCallmonitorDomain } from './fritzbox_callmonitor.types.js';

export class FritzboxCallmonitorIntegration extends BaseIntegration<IFritzboxCallmonitorConfig> {
  public readonly domain = fritzboxCallmonitorDomain;
  public readonly displayName = 'FRITZ!Box Call Monitor';
  public readonly status = 'read-only-runtime' as const;
  public readonly discoveryDescriptor = createFritzboxCallmonitorDiscoveryDescriptor();
  public readonly configFlow = new FritzboxCallmonitorConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/fritzbox_callmonitor',
    upstreamDomain: fritzboxCallmonitorDomain,
    integrationType: 'device',
    iotClass: 'local_polling',
    requirements: ['fritzconnection[qr]==1.15.1'],
    dependencies: [] as string[],
    afterDependencies: [] as string[],
    codeowners: [] as string[],
    documentation: 'https://www.home-assistant.io/integrations/fritzbox_callmonitor',
    configFlow: true,
    runtime: {
      mode: 'native TypeScript read-only snapshot/event runtime',
      platforms: ['sensor'],
      services: ['snapshot', 'status', 'refresh', 'process_event', 'start_monitor', 'stop_monitor'],
    },
    localApi: {
      implemented: [
        'manual setup, snapshot, contacts, prefixes, and raw call monitor event lines',
        'FRITZ!Box TCP call monitor line parsing for RING, CALL, CONNECT, and DISCONNECT',
        'optional live TCP subscription on port 1012 when config.host is supplied',
        'injected nativeClient, eventSource, or eventExecutor event streams',
      ],
      explicitUnsupported: [
        'fake live event success without config.host, nativeClient.subscribe, eventSource, or eventExecutor',
        'native fritzconnection/TR-064 phonebook download and authentication',
        'call control or other write actions; this integration is read-only/event based',
      ],
    },
  };

  public async setup(configArg: IFritzboxCallmonitorConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new FritzboxCallmonitorRuntime(new FritzboxCallmonitorClient(configArg));
  }

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

export class HomeAssistantFritzboxCallmonitorIntegration extends FritzboxCallmonitorIntegration {}

class FritzboxCallmonitorRuntime implements IIntegrationRuntime {
  public domain = fritzboxCallmonitorDomain;

  constructor(private readonly client: FritzboxCallmonitorClient) {}

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

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

  public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void): Promise<() => Promise<void>> {
    const unsubscribe = this.client.onEvent((eventArg) => handlerArg(FritzboxCallmonitorMapper.toIntegrationEvent(eventArg)));
    const startResult = await this.client.startMonitoring();
    if (!startResult.success) {
      handlerArg({
        type: 'error',
        integrationDomain: fritzboxCallmonitorDomain,
        data: startResult,
        timestamp: Date.now(),
      });
    }
    return async () => {
      unsubscribe();
      await this.client.stopMonitoring();
    };
  }

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.domain !== fritzboxCallmonitorDomain) {
      return { success: false, error: `Unsupported FRITZ!Box Call Monitor service domain: ${requestArg.domain}` };
    }

    if (requestArg.service === 'snapshot' || requestArg.service === 'status') {
      return { success: true, data: await this.client.getSnapshot() };
    }
    if (requestArg.service === 'refresh') {
      return this.client.refresh();
    }
    if (requestArg.service === 'start_monitor' || requestArg.service === 'listen') {
      return this.client.startMonitoring();
    }
    if (requestArg.service === 'stop_monitor') {
      return this.client.stopMonitoring();
    }
    if (requestArg.service === 'process_event' || requestArg.service === 'parse_event') {
      const event = this.stringValue(requestArg.data?.line) || this.stringValue(requestArg.data?.event) || this.stringValue(requestArg.data?.raw);
      if (!event) {
        return { success: false, error: 'FRITZ!Box Call Monitor process_event requires data.line, data.event, or data.raw.' };
      }
      return this.client.processEvent(event, 'manual');
    }

    return { success: false, error: `Unsupported FRITZ!Box Call Monitor service: ${requestArg.service}` };
  }

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

  private stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
  }
}
