import type {
  IBoschShcCommandContext,
  IBoschShcConfig,
  IBoschShcModeledCommand,
  IBoschShcPairClientRequest,
  IBoschShcPublicInformation,
  IBoschShcSnapshot,
} from './bosch_shc.types.js';

export class BoschShcUnsupportedLiveOperationError extends Error {
  constructor(actionArg: string) {
    super(`Bosch SHC ${actionArg} requires config.snapshot or an injected executor. This native TypeScript port does not implement live TLS, certificate generation, or pairing transport.`);
    this.name = 'BoschShcUnsupportedLiveOperationError';
  }
}

export class BoschShcClient {
  private readonly snapshot?: IBoschShcSnapshot;

  constructor(private readonly config: IBoschShcConfig) {
    this.snapshot = config.snapshot ? this.clone(config.snapshot) : undefined;
  }

  public async getSnapshot(): Promise<IBoschShcSnapshot> {
    if (this.snapshot) {
      return this.normalizeSnapshot(this.clone(this.snapshot), 'snapshot');
    }
    const result = await this.execute({
      action: 'read_snapshot',
      method: 'GET',
      path: '/smarthome',
      reason: 'read_snapshot',
    });
    if (!this.isSnapshot(result)) {
      throw new Error('Bosch SHC snapshot executor must return an IBoschShcSnapshot object for read_snapshot.');
    }
    return this.normalizeSnapshot(result, 'executor');
  }

  public async getPublicInformation(): Promise<IBoschShcPublicInformation> {
    if (this.snapshot?.information) {
      return this.clone(this.snapshot.information);
    }
    const result = await this.execute({
      action: 'get_public_information',
      method: 'GET',
      path: '/smarthome/public/information',
      reason: 'get_public_information',
    });
    if (typeof result !== 'object' || result === null || Array.isArray(result)) {
      throw new Error('Bosch SHC public information executor must return an object.');
    }
    return result as IBoschShcPublicInformation;
  }

  public async pairClient(requestArg: IBoschShcPairClientRequest): Promise<unknown> {
    if (!requestArg.systemPassword) {
      throw new Error('Bosch SHC pairing requires the controller system password.');
    }
    return this.execute({
      action: 'pair_client',
      method: 'POST',
      path: '/smarthome/clients',
      reason: 'pair_client',
      body: {
        clientId: requestArg.clientId || this.config.host || 'smarthome_exchange',
        clientName: requestArg.clientName || 'smarthome.exchange',
        certificatePem: requestArg.certificatePem,
        systemPassword: requestArg.systemPassword,
      },
      sensitiveFields: ['systemPassword', 'certificatePem'],
    });
  }

  public async executeCommand(commandArg: IBoschShcModeledCommand, snapshotArg?: IBoschShcSnapshot): Promise<unknown> {
    return this.execute(commandArg, snapshotArg);
  }

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

  private async execute(commandArg: IBoschShcModeledCommand, snapshotArg?: IBoschShcSnapshot): Promise<unknown> {
    const executor = this.config.executor;
    if (!executor) {
      throw new BoschShcUnsupportedLiveOperationError(commandArg.action);
    }
    const context: IBoschShcCommandContext = {
      config: this.config,
      snapshot: snapshotArg || this.snapshot,
    };
    if (typeof executor === 'function') {
      return executor(commandArg, context);
    }
    return executor.execute(commandArg, context);
  }

  private normalizeSnapshot(snapshotArg: IBoschShcSnapshot, sourceArg: IBoschShcSnapshot['source']): IBoschShcSnapshot {
    return {
      ...snapshotArg,
      devices: snapshotArg.devices || [],
      host: snapshotArg.host || this.config.host || snapshotArg.information?.shcIpAddress,
      uniqueId: snapshotArg.uniqueId || this.config.uniqueId || snapshotArg.information?.macAddress,
      name: snapshotArg.name || this.config.name || this.config.hostname || snapshotArg.information?.shcIpAddress,
      online: snapshotArg.online !== false,
      source: snapshotArg.source || sourceArg,
      updatedAt: snapshotArg.updatedAt || new Date().toISOString(),
    };
  }

  private isSnapshot(valueArg: unknown): valueArg is IBoschShcSnapshot {
    return typeof valueArg === 'object' && valueArg !== null && 'devices' in valueArg;
  }

  private clone<TValue>(valueArg: TValue): TValue {
    return JSON.parse(JSON.stringify(valueArg)) as TValue;
  }
}
