import { prisma, ExposureSource, ExposureSeverity, WatchlistType, AlertType, AlertSeverity } from '@shieldsai/shared-db';
import { createHash } from 'crypto';
import { mixpanelService, EventType } from '@shieldsai/shared-analytics';

function hashIdentifier(identifier: string): string {
  return createHash('sha256').update(identifier.toLowerCase().trim()).digest('hex');
}

function determineSeverity(
  source: ExposureSource,
  dataType: WatchlistType
): ExposureSeverity {
  const criticalSources = [ExposureSource.darkWebForum, ExposureSource.honeypot];
  const warningSources = [ExposureSource.hibp, ExposureSource.shodan];
  const criticalTypes = [WatchlistType.ssn];

  if (criticalTypes.includes(dataType)) return ExposureSeverity.critical;
  if (criticalSources.includes(source)) return ExposureSeverity.critical;
  if (warningSources.includes(source)) return ExposureSeverity.warning;
  return ExposureSeverity.info;
}

export interface WebhookPayload {
  source: string;
  identifier: string;
  identifierType: string;
  metadata?: Record<string, unknown>;
  timestamp?: string;
}

export class WebhookService {
  async processExternalWebhook(payload: WebhookPayload): Promise<{
    exposuresCreated: number;
    alertsCreated: number;
  }> {
    const source = this.mapSource(payload.source);
    const dataType = this.mapDataType(payload.identifierType);
    const identifier = payload.identifier.toLowerCase().trim();
    const identifierHash = hashIdentifier(identifier);
    const severity = determineSeverity(source, dataType);

    const matchingItems = await prisma.watchlistItem.findMany({
      where: {
        isActive: true,
        OR: [
          { hash: identifierHash, type: dataType },
          { value: identifier, type: dataType },
        ],
      },
      include: {
        subscription: {
          select: {
            id: true,
            tier: true,
            userId: true,
          },
        },
      },
    });

    let exposuresCreated = 0;
    let alertsCreated = 0;

    for (const item of matchingItems) {
      const existing = await prisma.exposure.findFirst({
        where: {
          subscriptionId: item.subscriptionId,
          source,
          identifierHash,
        },
      });

      if (existing) {
        await prisma.exposure.update({
          where: { id: existing.id },
          data: { detectedAt: new Date() },
        });
        continue;
      }

      const exposure = await prisma.exposure.create({
        data: {
          subscriptionId: item.subscriptionId,
          watchlistItemId: item.id,
          source,
          dataType,
          identifier,
          identifierHash,
          severity,
          isFirstTime: true,
          metadata: payload.metadata || {},
          detectedAt: new Date(),
        },
      });

      exposuresCreated++;

      const alertChannels = this.getAlertChannelsForTier(item.subscription.tier);

      await prisma.alert.create({
        data: {
          subscriptionId: item.subscriptionId,
          userId: item.subscription.userId,
          exposureId: exposure.id,
          type: AlertType.exposure_detected,
          title: `New Exposure Detected: ${this.getSourceLabel(source)}`,
          message: this.buildAlertMessage(identifier, source, severity),
          severity: this.mapAlertSeverity(severity),
          channel: alertChannels,
        },
      });

      alertsCreated++;

      await mixpanelService.track(EventType.EXPOSURE_DETECTED, {
        userId: item.subscription.userId,
        exposureType: dataType,
        severity,
        source,
        subscriptionTier: item.subscription.tier,
      });
    }

    return { exposuresCreated, alertsCreated };
  }

  async verifyWebhookSignature(
    body: string,
    signature: string,
    timestamp: string
  ): Promise<boolean> {
    const webhookSecret = process.env.DARKWATCH_WEBHOOK_SECRET;
    if (!webhookSecret) {
      console.warn('[WebhookService] DARKWATCH_WEBHOOK_SECRET not set — signature verification skipped');
      return false;
    }

    const expected = createHash('sha256')
      .update(`${timestamp}:${body}`)
      .digest('hex');

    return expected === signature;
  }

  private mapSource(source: string): ExposureSource {
    const sourceMap: Record<string, ExposureSource> = {
      hibp: ExposureSource.hibp,
      'haveibeenpwned': ExposureSource.hibp,
      securitytrails: ExposureSource.securityTrails,
      censys: ExposureSource.censys,
      'darkweb-forum': ExposureSource.darkWebForum,
      'darkweb': ExposureSource.darkWebForum,
      shodan: ExposureSource.shodan,
      honeypot: ExposureSource.honeypot,
    };

    const normalized = source.toLowerCase().replace(/\s+/g, '');
    const mapped = sourceMap[normalized];
    if (!mapped) {
      console.warn(`[WebhookService] Unknown source "${source}", falling back to darkWebForum`);
    }
    return mapped || ExposureSource.darkWebForum;
  }

  private mapDataType(type: string): WatchlistType {
    const typeMap: Record<string, WatchlistType> = {
      email: WatchlistType.email,
      phone: WatchlistType.phoneNumber,
      phonenumber: WatchlistType.phoneNumber,
      ssn: WatchlistType.ssn,
      address: WatchlistType.address,
      domain: WatchlistType.domain,
    };

    const normalized = type.toLowerCase().trim();
    return typeMap[normalized] || WatchlistType.email;
  }

  private getAlertChannelsForTier(tier: string): string[] {
    const channelMap: Record<string, string[]> = {
      basic: ['email'],
      plus: ['email', 'push'],
      premium: ['email', 'push', 'sms'],
    };
    return channelMap[tier] || ['email'];
  }

  private mapAlertSeverity(severity: ExposureSeverity): AlertSeverity {
    return severity as AlertSeverity;
  }

  private getSourceLabel(source: ExposureSource): string {
    const labels: Record<ExposureSource, string> = {
      [ExposureSource.hibp]: 'Have I Been Pwned',
      [ExposureSource.securityTrails]: 'SecurityTrails',
      [ExposureSource.censys]: 'Censys',
      [ExposureSource.darkWebForum]: 'Dark Web Forum',
      [ExposureSource.shodan]: 'Shodan',
      [ExposureSource.honeypot]: 'Honeypot',
    };
    return labels[source] || source;
  }

  private buildAlertMessage(
    identifier: string,
    source: ExposureSource,
    severity: ExposureSeverity
  ): string {
    const masked = this.maskIdentifier(identifier);
    return `${severity.toUpperCase()}: "${masked}" found in ${this.getSourceLabel(source)}.`;
  }

  private maskIdentifier(identifier: string): string {
    if (identifier.includes('@')) {
      const [user, domain] = identifier.split('@');
      const maskedUser = user.slice(0, 2) + '***' + user.slice(-1);
      return `${maskedUser}@${domain}`;
    }
    if (identifier.length > 8) {
      return identifier.slice(0, 3) + '***' + identifier.slice(-2);
    }
    return identifier;
  }
}

export const webhookService = new WebhookService();
