import { bus } from '../../lib/event-bus.js';
import * as auditRepo from '../audit/repository.js';
import type { AuditDoc } from '../audit/types.js';
import { getById as getUserById } from '../auth/repository.js';
import { getSession } from './repository.js';
import { dispatchEmail, dispatchSlack } from '../delivery/dispatcher.js';
import { randomUUID } from 'node:crypto';

// ── Event Bus Subscribers for Diagnostics ────────────────────
// Handles notifications and audit logging for debug session lifecycle.
// Handlers are fire-and-forget — errors are logged, never thrown.

const noopLog = {
  info: (..._a: unknown[]) => {},
  warn: (..._a: unknown[]) => {},
  error: (..._a: unknown[]) => {},
};

/**
 * Register all diagnostics-related event subscribers.
 * Call this once at service startup.
 */
export function registerDiagnosticsSubscribers(
  log: { info: (...a: unknown[]) => void; error: (...a: unknown[]) => void } = noopLog
): void {
  // Session created → audit log + notify target user (email/push)
  bus.on('diagnostics.session.created', async event => {
    try {
      // Audit log
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: event.payload.createdBy,
        action: 'diagnostics.session.created',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          targetUserId: event.payload.targetUserId,
          targetAnonymousId: event.payload.targetAnonymousId,
          targetDeviceId: event.payload.targetDeviceId,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);

      // Notify the target user via email if they have an account
      if (event.payload.targetUserId) {
        try {
          const targetUser = await getUserById(event.payload.targetUserId);
          if (targetUser) {
            const session = await getSession(event.payload.sessionId);
            await dispatchEmail(
              {
                to: targetUser.email,
                templateId: 'diagnostics-session-created',
                variables: {
                  displayName: targetUser.displayName || targetUser.email.split('@')[0],
                  productName: event.payload.productId,
                  sessionId: event.payload.sessionId,
                  startedAt: new Date().toISOString(),
                  maxDurationMinutes: String(session?.maxDurationMinutes ?? 30),
                },
                productId: event.payload.productId,
                userId: event.payload.targetUserId,
              },
              log
            );
          }
        } catch (notifyErr) {
          log.error(
            { notifyErr },
            '[diagnostics/subscriber] Failed to notify target user on create'
          );
        }
      }
      log.info(
        { sessionId: event.payload.sessionId, targetUserId: event.payload.targetUserId },
        '[diagnostics/subscriber] Session created, user notified'
      );
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle session.created'
      );
    }
  });

  // Session started → audit log
  bus.on('diagnostics.session.started', async event => {
    try {
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: 'system',
        action: 'diagnostics.session.started',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          startedAt: event.payload.startedAt,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle session.started'
      );
    }
  });

  // Session updated → audit log
  bus.on('diagnostics.session.updated', async event => {
    try {
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: event.payload.updatedBy,
        action: 'diagnostics.session.updated',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          changes: event.payload.changes,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle session.updated'
      );
    }
  });

  // Session cancelled → audit log + notify admin
  bus.on('diagnostics.session.cancelled', async event => {
    try {
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: event.payload.cancelledBy,
        action: 'diagnostics.session.cancelled',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          reason: event.payload.reason,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);

      // Notify admin who created the session
      try {
        const session = await getSession(event.payload.sessionId);
        if (session?.createdBy) {
          const admin = await getUserById(session.createdBy);
          if (admin) {
            await dispatchEmail(
              {
                to: admin.email,
                templateId: 'diagnostics-session-cancelled',
                variables: {
                  productName: event.payload.productId,
                  sessionId: event.payload.sessionId,
                  targetUserId: session.targetUserId ?? 'unknown',
                  cancelledBy: event.payload.cancelledBy,
                  reason: event.payload.reason ?? 'No reason provided',
                  cancelledAt: new Date().toISOString(),
                },
                productId: event.payload.productId,
                userId: session.createdBy,
              },
              log
            );
          }
        }
      } catch (notifyErr) {
        log.error({ notifyErr }, '[diagnostics/subscriber] Failed to notify admin on cancel');
      }
      log.info(
        { sessionId: event.payload.sessionId, cancelledBy: event.payload.cancelledBy },
        '[diagnostics/subscriber] Session cancelled, admin notified'
      );
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle session.cancelled'
      );
    }
  });

  // Session completed → audit log + email summary to admin
  bus.on('diagnostics.session.completed', async event => {
    try {
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: 'system',
        action: 'diagnostics.session.completed',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          stats: event.payload.stats,
          endedAt: event.payload.endedAt,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);

      // Email summary to admin who created the session
      try {
        const session = await getSession(event.payload.sessionId);
        if (session?.createdBy) {
          const admin = await getUserById(session.createdBy);
          if (admin) {
            const stats = event.payload.stats;
            const startedAt = session.startedAt ?? session.createdAt;
            const endedAt = event.payload.endedAt;
            const durationMs =
              startedAt && endedAt
                ? new Date(endedAt).getTime() - new Date(startedAt).getTime()
                : 0;
            const durationMinutes = String(Math.round(durationMs / 60_000));
            await dispatchEmail(
              {
                to: admin.email,
                templateId: 'diagnostics-session-completed',
                variables: {
                  productName: event.payload.productId,
                  sessionId: event.payload.sessionId,
                  durationMinutes,
                  logCount: String(stats.logCount),
                  traceCount: String(stats.traceCount),
                  screenshotCount: String(stats.screenshotCount),
                },
                productId: event.payload.productId,
                userId: session.createdBy,
              },
              log
            );
          }
        }
      } catch (notifyErr) {
        log.error({ notifyErr }, '[diagnostics/subscriber] Failed to email summary on complete');
      }
      log.info(
        { sessionId: event.payload.sessionId, stats: event.payload.stats },
        '[diagnostics/subscriber] Session completed, summary emailed'
      );
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle session.completed'
      );
    }
  });

  // Session expired (TTL job) → audit log
  bus.on('diagnostics.session.expired', async event => {
    try {
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: 'system',
        action: 'diagnostics.session.expired',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          expiredAt: event.payload.expiredAt,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle session.expired'
      );
    }
  });

  // Fatal log ingested → alert on-call engineer (PagerDuty/Slack)
  bus.on('diagnostics.ingest.fatal', async event => {
    try {
      // Audit log the fatal event
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: 'system',
        action: 'diagnostics.ingest.fatal',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          logEntryId: event.payload.logEntry?.id,
          timestamp: event.payload.timestamp,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);

      // Send Slack alert for on-call engineer
      try {
        const entry = event.payload.logEntry;
        const text = [
          `:rotating_light: *FATAL log during debug session*`,
          `*Session:* ${event.payload.sessionId}`,
          `*Product:* ${event.payload.productId}`,
          `*Timestamp:* ${event.payload.timestamp}`,
          entry?.message ? `*Message:* ${entry.message}` : '',
        ]
          .filter(Boolean)
          .join('\n');
        await dispatchSlack({
          text,
          productId: event.payload.productId,
          username: 'DiagnosticsBot',
        });
      } catch (slackErr) {
        log.error({ slackErr }, '[diagnostics/subscriber] Failed to send Slack alert for FATAL');
      }
      log.error(
        { sessionId: event.payload.sessionId, logEntry: event.payload.logEntry },
        '[diagnostics/subscriber] FATAL log ingested — Slack alert sent'
      );
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle ingest.fatal'
      );
    }
  });

  // Screenshot captured → audit log
  bus.on('diagnostics.screenshot.captured', async event => {
    try {
      const auditDoc: AuditDoc = {
        id: `aud_${randomUUID()}`,
        productId: event.payload.productId,
        userId: 'system',
        action: 'diagnostics.screenshot.captured',
        category: 'diagnostics',
        details: {
          sessionId: event.payload.sessionId,
          screenshotId: event.payload.screenshotId,
          trigger: event.payload.trigger,
        },
        createdAt: new Date().toISOString(),
      };
      await auditRepo.create(auditDoc);
    } catch (err) {
      log.error(
        { err, eventId: event.id },
        '[diagnostics/subscriber] Failed to handle screenshot.captured'
      );
    }
  });

  log.info('[diagnostics] Registered event bus subscribers');
}
