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 { PrusaLinkClient } from './prusalink.classes.client.js';
import { PrusaLinkConfigFlow } from './prusalink.classes.configflow.js';
import { createPrusaLinkDiscoveryDescriptor } from './prusalink.discovery.js';
import { PrusaLinkMapper } from './prusalink.mapper.js';
import type { IPrusaLinkConfig } from './prusalink.types.js';
import { prusaLinkDomain } from './prusalink.types.js';

export class PrusaLinkIntegration extends BaseIntegration<IPrusaLinkConfig> {
  public readonly domain = prusaLinkDomain;
  public readonly displayName = 'PrusaLink';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createPrusaLinkDiscoveryDescriptor();
  public readonly configFlow = new PrusaLinkConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/prusalink',
    upstreamDomain: prusaLinkDomain,
    integrationType: 'device',
    iotClass: 'local_polling',
    requirements: ['pyprusalink==2.1.1'],
    dependencies: [] as string[],
    afterDependencies: [] as string[],
    codeowners: [] as string[],
    configFlow: true,
    documentation: 'https://www.home-assistant.io/integrations/prusalink',
    dhcp: [{ macaddress: '109C70*' }],
    discovery: {
      manual: true,
      http: 'Manual local HTTP endpoint candidates are recognized; no active LAN scan is performed.',
      note: 'PrusaLink has HA DHCP discovery only. Configure host/credentials or provide raw API data, an injected client, or a snapshot.',
    },
    runtime: {
      type: 'control-runtime',
      polling: 'local HTTP PrusaLink API /api/version, /api/v1/status, /api/v1/job, /api/v1/info, and /api/printer',
      services: ['snapshot', 'status', 'refresh', 'pause_job', 'resume_job', 'cancel_job', 'restart_job'],
      controls: true,
    },
    localApi: {
      implemented: [
        'PrusaLink HTTP API with Digest authentication compatible with pyprusalink 2.1.1 endpoints used by Home Assistant',
        'native snapshot mapping for printer state, job, temperatures, progress, material, MMU, and represented buttons',
        'safe pause/resume/cancel job commands over native HTTP or an injected command executor',
        'restart job command modeling through an injected executor only; no native HTTP endpoint is represented upstream',
      ],
      explicitUnsupported: [
        'Home Assistant Python pyprusalink compatibility wrapper',
        'fake live API or command success without a configured HTTP endpoint, injected client, snapshot/manual data, or command executor',
      ],
    },
  };

  public async setup(configArg: IPrusaLinkConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new PrusaLinkRuntime(new PrusaLinkClient(configArg));
  }

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

export class HomeAssistantPrusalinkIntegration extends PrusaLinkIntegration {}

class PrusaLinkRuntime implements IIntegrationRuntime {
  public domain = prusaLinkDomain;

  constructor(private readonly client: PrusaLinkClient) {}

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

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

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === prusaLinkDomain && (requestArg.service === 'snapshot' || requestArg.service === 'status')) {
        return { success: true, data: await this.client.getSnapshot() };
      }
      if (requestArg.domain === prusaLinkDomain && 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 = PrusaLinkMapper.commandForService(snapshot, requestArg);
      if (!command) {
        return { success: false, error: `Unsupported PrusaLink service: ${requestArg.domain}.${requestArg.service}` };
      }
      const data = await this.client.execute(command);
      return { success: true, data: data ?? command };
    } catch (errorArg) {
      return { success: false, error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
    }
  }

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