import { EventEmitter } from '../event-emitter.js'
import type { Collection } from './index.js'
import type { CollectionStatus } from '../types.js'
import type { BasicExpression } from '../query/ir.js'

/**
 * Event emitted when the collection status changes
 */
export interface CollectionStatusChangeEvent {
  type: `status:change`
  collection: Collection
  previousStatus: CollectionStatus
  status: CollectionStatus
}

/**
 * Event emitted when the collection status changes to a specific status
 */
export interface CollectionStatusEvent<T extends CollectionStatus> {
  type: `status:${T}`
  collection: Collection
  previousStatus: CollectionStatus
  status: T
}

/**
 * Event emitted when the number of subscribers to the collection changes
 */
export interface CollectionSubscribersChangeEvent {
  type: `subscribers:change`
  collection: Collection
  previousSubscriberCount: number
  subscriberCount: number
}

/**
 * Event emitted when the collection's loading more state changes
 */
export interface CollectionLoadingSubsetChangeEvent {
  type: `loadingSubset:change`
  collection: Collection<any, any, any, any, any>
  isLoadingSubset: boolean
  previousIsLoadingSubset: boolean
  loadingSubsetTransition: `start` | `end`
}

/**
 * Event emitted when the collection is truncated (all data cleared)
 */
export interface CollectionTruncateEvent {
  type: `truncate`
  collection: Collection<any, any, any, any, any>
}

export type CollectionIndexSerializableValue =
  | string
  | number
  | boolean
  | null
  | Array<CollectionIndexSerializableValue>
  | {
      [key: string]: CollectionIndexSerializableValue
    }

export interface CollectionIndexResolverMetadata {
  kind: `constructor` | `async`
  name?: string
}

export interface CollectionIndexMetadata {
  /**
   * Version for the signature serialization contract.
   */
  signatureVersion: 1
  /**
   * Stable signature derived from expression + serializable options.
   * Non-serializable option fields are intentionally omitted.
   */
  signature: string
  indexId: number
  name?: string
  expression: BasicExpression
  resolver: CollectionIndexResolverMetadata
  options?: CollectionIndexSerializableValue
}

export interface CollectionIndexAddedEvent {
  type: `index:added`
  collection: Collection<any, any, any, any, any>
  index: CollectionIndexMetadata
}

export interface CollectionIndexRemovedEvent {
  type: `index:removed`
  collection: Collection<any, any, any, any, any>
  index: CollectionIndexMetadata
}

export type AllCollectionEvents = {
  'status:change': CollectionStatusChangeEvent
  'subscribers:change': CollectionSubscribersChangeEvent
  'loadingSubset:change': CollectionLoadingSubsetChangeEvent
  truncate: CollectionTruncateEvent
  'index:added': CollectionIndexAddedEvent
  'index:removed': CollectionIndexRemovedEvent
} & {
  [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>
}

export type CollectionEvent =
  | AllCollectionEvents[keyof AllCollectionEvents]
  | CollectionStatusChangeEvent
  | CollectionSubscribersChangeEvent
  | CollectionLoadingSubsetChangeEvent
  | CollectionTruncateEvent
  | CollectionIndexAddedEvent
  | CollectionIndexRemovedEvent

export type CollectionEventHandler<T extends keyof AllCollectionEvents> = (
  event: AllCollectionEvents[T],
) => void

export class CollectionEventsManager extends EventEmitter<AllCollectionEvents> {
  private collection!: Collection<any, any, any, any, any>

  constructor() {
    super()
  }

  setDeps(deps: { collection: Collection<any, any, any, any, any> }) {
    this.collection = deps.collection
  }

  /**
   * Emit an event to all listeners
   * Public API for emitting collection events
   */
  emit<T extends keyof AllCollectionEvents>(
    event: T,
    eventPayload: AllCollectionEvents[T],
  ): void {
    this.emitInner(event, eventPayload)
  }

  emitStatusChange<T extends CollectionStatus>(
    status: T,
    previousStatus: CollectionStatus,
  ) {
    this.emit(`status:change`, {
      type: `status:change`,
      collection: this.collection,
      previousStatus,
      status,
    })

    // Emit specific status event using type assertion
    const eventKey: `status:${T}` = `status:${status}`
    this.emit(eventKey, {
      type: eventKey,
      collection: this.collection,
      previousStatus,
      status,
    } as AllCollectionEvents[`status:${T}`])
  }

  emitSubscribersChange(
    subscriberCount: number,
    previousSubscriberCount: number,
  ) {
    this.emit(`subscribers:change`, {
      type: `subscribers:change`,
      collection: this.collection,
      previousSubscriberCount,
      subscriberCount,
    })
  }

  emitIndexAdded(index: CollectionIndexMetadata) {
    this.emit(`index:added`, {
      type: `index:added`,
      collection: this.collection,
      index,
    })
  }

  emitIndexRemoved(index: CollectionIndexMetadata) {
    this.emit(`index:removed`, {
      type: `index:removed`,
      collection: this.collection,
      index,
    })
  }

  cleanup() {
    this.clearListeners()
  }
}
