import {
  defaultTrapRuntime,
  type SceneTrapsEvent,
  type SceneTrapsState,
  type SceneTrapRuntime,
} from '../../shared/types';

function emptyState(): SceneTrapsState {
  return {
    revision: 1,
    cacheKey: null,
    byId: {},
    lastActivation: null,
  };
}

function cloneById(byId: Record<string, SceneTrapRuntime>): Record<string, SceneTrapRuntime> {
  const out: Record<string, SceneTrapRuntime> = {};
  for (const [k, v] of Object.entries(byId)) {
    out[k] = { ...v };
  }
  return out;
}

export class SceneTrapsStore {
  private state: SceneTrapsState = emptyState();
  /** Кэш runtime по ключу сцены/ноды на время сессии показа. */
  private cache = new Map<string, Record<string, SceneTrapRuntime>>();
  private currentKey: string | null = null;
  private activationToken = 0;

  getState(): SceneTrapsState {
    return {
      ...this.state,
      byId: cloneById(this.state.byId),
      lastActivation: this.state.lastActivation ? { ...this.state.lastActivation } : null,
    };
  }

  resetSession(): void {
    this.cache.clear();
    this.currentKey = null;
    this.state = emptyState();
  }

  /**
   * Переключение сцены: сохраняем runtime в кэш и подгружаем/инициализируем для новых trapIds.
   */
  switchScene(cacheKey: string | null, trapIds: readonly string[]): SceneTrapsState {
    if (this.currentKey !== null) {
      this.cache.set(this.currentKey, cloneById(this.state.byId));
    }
    this.currentKey = cacheKey;
    const cached = cacheKey ? this.cache.get(cacheKey) : undefined;
    const byId: Record<string, SceneTrapRuntime> = {};
    for (const id of trapIds) {
      byId[id] = cached?.[id] ? { ...cached[id]! } : defaultTrapRuntime();
    }
    if (cacheKey) {
      this.cache.set(cacheKey, cloneById(byId));
    }
    this.state = {
      revision: this.state.revision + 1,
      cacheKey,
      byId,
      lastActivation: null,
    };
    return this.getState();
  }

  dispatch(event: SceneTrapsEvent): SceneTrapsState {
    switch (event.kind) {
      case 'syncTrapIds': {
        const byId: Record<string, SceneTrapRuntime> = {};
        for (const id of event.trapIds) {
          byId[id] = this.state.byId[id] ? { ...this.state.byId[id]! } : defaultTrapRuntime();
        }
        if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
        this.state = {
          ...this.state,
          revision: this.state.revision + 1,
          byId,
        };
        return this.getState();
      }
      case 'reveal': {
        const cur = this.state.byId[event.trapId];
        if (!cur) return this.getState();
        const byId = cloneById(this.state.byId);
        byId[event.trapId] = { ...cur, revealed: true };
        if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
        this.state = {
          ...this.state,
          revision: this.state.revision + 1,
          byId,
        };
        return this.getState();
      }
      case 'activate': {
        const cur = this.state.byId[event.trapId];
        if (!cur) return this.getState();
        const byId = cloneById(this.state.byId);
        byId[event.trapId] = { status: 'active', revealed: true };
        this.activationToken += 1;
        if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
        this.state = {
          ...this.state,
          revision: this.state.revision + 1,
          byId,
          lastActivation: { trapId: event.trapId, token: this.activationToken },
        };
        return this.getState();
      }
      case 'disarm': {
        const cur = this.state.byId[event.trapId];
        if (!cur) return this.getState();
        const byId = cloneById(this.state.byId);
        byId[event.trapId] = { status: 'disarmed', revealed: true };
        if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
        this.state = {
          ...this.state,
          revision: this.state.revision + 1,
          byId,
        };
        return this.getState();
      }
      default: {
        const _x: never = event;
        void _x;
        return this.getState();
      }
    }
  }
}
