import { EventEmitter } from "node:events";

import type {
  RiskAllocation,
  SymbolConfig,
  SymbolRuntimeState,
  SymbolStatus
} from "./types";

export interface RegistryEvents {
  registered: SymbolRuntimeState;
  updated: SymbolRuntimeState;
  statusChanged: SymbolRuntimeState;
  removed: { symbol: string };
}

type EventKey = keyof RegistryEvents;

export class SymbolRegistry extends EventEmitter {
  private readonly symbols = new Map<string, SymbolRuntimeState>();

  constructor(initialConfigs: SymbolConfig[] = []) {
    super();
    initialConfigs.forEach(cfg => this.register(cfg));
  }

  register(config: SymbolConfig): SymbolRuntimeState {
    const prev = this.symbols.get(config.symbol);
    const state: SymbolRuntimeState = {
      config: { ...config },
      status: config.enabled ?? true ? "inactive" : "disabled",
      updatedAt: Date.now()
    };
    this.symbols.set(config.symbol, state);
    this.emit("registered", state);
    if (prev) {
      this.emit("updated", state);
    }
    return state;
  }

  updateConfig(symbol: string, patch: Partial<SymbolConfig>): SymbolRuntimeState {
    const state = this.require(symbol);
    state.config = { ...state.config, ...patch, symbol };
    state.updatedAt = Date.now();
    this.emit("updated", state);
    return state;
  }

  setStatus(symbol: string, status: SymbolStatus, reason?: string): SymbolRuntimeState {
    const state = this.require(symbol);
    if (state.status === status && state.reason === reason) {
      return state;
    }
    state.status = status;
    state.reason = reason;
    state.updatedAt = Date.now();
    this.emit("statusChanged", state);
    return state;
  }

  activate(symbol: string): SymbolRuntimeState {
    return this.setStatus(symbol, "active");
  }

  pause(symbol: string, reason: string): SymbolRuntimeState {
    return this.setStatus(symbol, "paused", reason);
  }

  disable(symbol: string, reason?: string): SymbolRuntimeState {
    return this.setStatus(symbol, "disabled", reason);
  }

  remove(symbol: string): void {
    if (!this.symbols.has(symbol)) {
      return;
    }
    this.symbols.delete(symbol);
    this.emit("removed", { symbol });
  }

  list(status?: SymbolStatus): SymbolRuntimeState[] {
    const states = Array.from(this.symbols.values());
    if (!status) return states;
    return states.filter(s => s.status === status);
  }

  listActive(): SymbolRuntimeState[] {
    return this.list("active");
  }

  get(symbol: string): SymbolRuntimeState | undefined {
    return this.symbols.get(symbol);
  }

  setAllocation(symbol: string, allocation: RiskAllocation | undefined): SymbolRuntimeState {
    const state = this.require(symbol);
    state.allocation = allocation;
    state.updatedAt = Date.now();
    this.emit("updated", state);
    return state;
  }

  setScore(symbol: string, score: number | undefined): SymbolRuntimeState {
    const state = this.require(symbol);
    state.score = score;
    state.updatedAt = Date.now();
    this.emit("updated", state);
    return state;
  }

  on<Event extends EventKey>(event: Event, listener: (payload: RegistryEvents[Event]) => void): this {
    return super.on(event, listener);
  }

  once<Event extends EventKey>(event: Event, listener: (payload: RegistryEvents[Event]) => void): this {
    return super.once(event, listener);
  }

  emit<Event extends EventKey>(event: Event, payload: RegistryEvents[Event]): boolean {
    return super.emit(event, payload);
  }

  private require(symbol: string): SymbolRuntimeState {
    const state = this.symbols.get(symbol);
    if (!state) {
      throw new Error(`Symbol ${symbol} is not registered`);
    }
    return state;
  }
}
