import type { IKnxClientCommand, IKnxCommandResult, IKnxConfig, IKnxEvent, IKnxSnapshot } from './knx.types.js';
import { KnxMapper } from './knx.mapper.js';

type TKnxEventHandler = (eventArg: IKnxEvent) => void;

export class KnxClient {
  private readonly events: IKnxEvent[] = [];
  private readonly eventHandlers = new Set<TKnxEventHandler>();

  constructor(private readonly config: IKnxConfig) {}

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

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

  public async sendCommand(commandArg: IKnxClientCommand): Promise<IKnxCommandResult> {
    this.emit({ type: 'command_mapped', command: commandArg, 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: 'KNX live bus telegrams require the xknx KNXnet/IP stack and DPT transcoding. This dependency-free TypeScript port maps commands but does not send them to the bus.',
      data: { command: commandArg },
    };
  }

  public async connectLive(): Promise<void> {
    throw new Error(this.unsupportedMessage());
  }

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

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

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

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

  private unsupportedMessage(): string {
    const connectionType = this.config.connectionType || this.config.connection_type || (this.config.host ? 'tunneling' : 'automatic');
    const target = this.config.host ? `${this.config.host}:${this.config.port || 3671}` : connectionType;
    return `KNX live bus control for ${target} requires xknx-compatible KNXnet/IP transport and DPT conversion. This TypeScript port intentionally does not guess those internals.`;
  }
}
