import type { IXiaomiMiioClientCommand, IXiaomiMiioCommandResult, IXiaomiMiioConfig, IXiaomiMiioEvent, IXiaomiMiioSnapshot } from './xiaomi_miio.types.js';
import { XiaomiMiioMapper } from './xiaomi_miio.mapper.js';

type TXiaomiMiioEventHandler = (eventArg: IXiaomiMiioEvent) => void;

export class XiaomiMiioClient {
  private readonly events: IXiaomiMiioEvent[] = [];
  private readonly eventHandlers = new Set<TXiaomiMiioEventHandler>();

  constructor(private readonly config: IXiaomiMiioConfig) {}

  public async getSnapshot(): Promise<IXiaomiMiioSnapshot> {
    return XiaomiMiioMapper.toSnapshot(this.config, undefined, this.events);
  }

  public onEvent(handlerArg: TXiaomiMiioEventHandler): () => void {
    this.eventHandlers.add(handlerArg);
    return () => this.eventHandlers.delete(handlerArg);
  }

  public async sendCommand(commandArg: IXiaomiMiioClientCommand): Promise<IXiaomiMiioCommandResult> {
    this.emit({ type: 'command_mapped', command: commandArg, deviceId: commandArg.deviceId, entityId: commandArg.entityId, uniqueId: commandArg.uniqueId, timestamp: Date.now() });
    if (this.config.commandExecutor) {
      const result = await this.config.commandExecutor(commandArg);
      return this.commandResult(result, commandArg);
    }
    return {
      success: false,
      error: `Xiaomi Miio live UDP writes for ${this.config.host || 'configured host'} require encrypted miIO packet framing and validated token handling. This dependency-free TypeScript port maps commands but does not send encrypted local writes without a commandExecutor.`,
      data: { command: commandArg },
    };
  }

  public async sendLocalCommand(methodArg: string, paramsArg: unknown[] = []): Promise<IXiaomiMiioCommandResult> {
    return this.sendCommand({
      type: 'local.miio.command',
      service: 'raw_command',
      method: methodArg,
      params: paramsArg,
      payload: { method: methodArg, params: paramsArg },
    });
  }

  public async destroy(): Promise<void> {
    this.eventHandlers.clear();
  }

  private emit(eventArg: IXiaomiMiioEvent): void {
    this.events.push(eventArg);
    for (const handler of this.eventHandlers) {
      handler(eventArg);
    }
  }

  private commandResult(resultArg: unknown, commandArg: IXiaomiMiioClientCommand): IXiaomiMiioCommandResult {
    if (this.isCommandResult(resultArg)) {
      return resultArg;
    }
    return { success: true, data: resultArg ?? { command: commandArg } };
  }

  private isCommandResult(valueArg: unknown): valueArg is IXiaomiMiioCommandResult {
    return typeof valueArg === 'object' && valueArg !== null && 'success' in valueArg;
  }
}
