import { BaseEntityService } from '../BaseEntityService';
import { IndexedDBContext } from '../IndexedDBContext';
import { IAuditEntry, IAuditLoggedPayload } from '../../types/AuditTypes';
import { EntityType, IEventBus, IEntitySavedPayload, IEntityDeletedPayload } from '../../types/CalendarTypes';
import { CoreEvents } from '../../constants/CoreEvents';

/**
 * AuditService - Entity service for audit entries
 *
 * RESPONSIBILITIES:
 * - Store audit entries in IndexedDB
 * - Listen for ENTITY_SAVED/ENTITY_DELETED events
 * - Create audit entries for all entity changes
 * - Emit AUDIT_LOGGED after saving (for SyncManager to listen)
 *
 * OVERRIDE PATTERN:
 * - Overrides save() to NOT emit events (prevents infinite loops)
 * - AuditService saves audit entries without triggering more audits
 *
 * EVENT CHAIN:
 * Entity change → ENTITY_SAVED/DELETED → AuditService → AUDIT_LOGGED → SyncManager
 */
export class AuditService extends BaseEntityService<IAuditEntry> {
  readonly storeName = 'audit';
  readonly entityType: EntityType = 'Audit';

  // Hardcoded userId for now - will come from session later
  private static readonly DEFAULT_USER_ID = '00000000-0000-0000-0000-000000000001';

  constructor(context: IndexedDBContext, eventBus: IEventBus) {
    super(context, eventBus);
    this.setupEventListeners();
  }

  /**
   * Setup listeners for ENTITY_SAVED and ENTITY_DELETED events
   */
  private setupEventListeners(): void {
    // Listen for entity saves (create/update)
    this.eventBus.on(CoreEvents.ENTITY_SAVED, (event: Event) => {
      const detail = (event as CustomEvent).detail;
      this.handleEntitySaved(detail);
    });

    // Listen for entity deletes
    this.eventBus.on(CoreEvents.ENTITY_DELETED, (event: Event) => {
      const detail = (event as CustomEvent).detail;
      this.handleEntityDeleted(detail);
    });
  }

  /**
   * Handle ENTITY_SAVED event - create audit entry
   */
  private async handleEntitySaved(payload: IEntitySavedPayload): Promise<void> {
    // Don't audit audit entries (prevent infinite loops)
    if (payload.entityType === 'Audit') return;

    const auditEntry: IAuditEntry = {
      id: crypto.randomUUID(),
      entityType: payload.entityType,
      entityId: payload.entityId,
      operation: payload.operation,
      userId: AuditService.DEFAULT_USER_ID,
      timestamp: payload.timestamp,
      changes: payload.changes,
      synced: false,
      syncStatus: 'pending'
    };

    await this.save(auditEntry);
  }

  /**
   * Handle ENTITY_DELETED event - create audit entry
   */
  private async handleEntityDeleted(payload: IEntityDeletedPayload): Promise<void> {
    // Don't audit audit entries (prevent infinite loops)
    if (payload.entityType === 'Audit') return;

    const auditEntry: IAuditEntry = {
      id: crypto.randomUUID(),
      entityType: payload.entityType,
      entityId: payload.entityId,
      operation: 'delete',
      userId: AuditService.DEFAULT_USER_ID,
      timestamp: payload.timestamp,
      changes: { id: payload.entityId }, // For delete, just store the ID
      synced: false,
      syncStatus: 'pending'
    };

    await this.save(auditEntry);
  }

  /**
   * Override save to NOT trigger ENTITY_SAVED event
   * Instead, emits AUDIT_LOGGED for SyncManager to listen
   *
   * This prevents infinite loops:
   * - BaseEntityService.save() emits ENTITY_SAVED
   * - AuditService listens to ENTITY_SAVED and creates audit
   * - If AuditService.save() also emitted ENTITY_SAVED, it would loop
   */
  async save(entity: IAuditEntry): Promise<void> {
    const serialized = this.serialize(entity);

    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.storeName], 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.put(serialized);

      request.onsuccess = () => {
        // Emit AUDIT_LOGGED instead of ENTITY_SAVED
        const payload: IAuditLoggedPayload = {
          auditId: entity.id,
          entityType: entity.entityType,
          entityId: entity.entityId,
          operation: entity.operation,
          timestamp: entity.timestamp
        };
        this.eventBus.emit(CoreEvents.AUDIT_LOGGED, payload);
        resolve();
      };

      request.onerror = () => {
        reject(new Error(`Failed to save audit entry ${entity.id}: ${request.error}`));
      };
    });
  }

  /**
   * Override delete to NOT trigger ENTITY_DELETED event
   * Audit entries should never be deleted (compliance requirement)
   */
  async delete(_id: string): Promise<void> {
    throw new Error('Audit entries cannot be deleted (compliance requirement)');
  }

  /**
   * Get pending audit entries (for sync)
   */
  async getPendingAudits(): Promise<IAuditEntry[]> {
    return this.getBySyncStatus('pending');
  }

  /**
   * Get audit entries for a specific entity
   */
  async getByEntityId(entityId: string): Promise<IAuditEntry[]> {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.storeName], 'readonly');
      const store = transaction.objectStore(this.storeName);
      const index = store.index('entityId');
      const request = index.getAll(entityId);

      request.onsuccess = () => {
        const entries = request.result as IAuditEntry[];
        resolve(entries);
      };

      request.onerror = () => {
        reject(new Error(`Failed to get audit entries for entity ${entityId}: ${request.error}`));
      };
    });
  }
}
