import { prisma, AlertSeverity, AlertChannel } from '@shieldai/db';
import {
  NotificationService,
  loadNotificationConfig,
} from '@shieldai/shared-notifications';
import {
  ChangeDetectionResult,
  ChangeType,
  Severity,
  PropertyAlert,
  AlertSeverityLevel,
  NotificationChannel,
  AlertPipelineConfig,
} from './types';

const DEFAULT_CONFIG: AlertPipelineConfig = {
  dedupWindowMs: 24 * 60 * 60 * 1000,
  minSeverity: 'moderate',
  premiumTierChannels: ['email', 'push', 'sms'],
  defaultChannels: ['email'],
};

const SEVERITY_MAP: Record<Severity, AlertSeverityLevel> = {
  major: 'critical',
  moderate: 'warning',
  minor: 'info',
};

const CHANGE_TYPE_LABELS: Record<ChangeType, string> = {
  ownership_transfer: 'Ownership Transfer',
  deed_change: 'Deed Change',
  lien_filing: 'Lien Filing',
  tax_change: 'Tax Assessment Change',
  metadata_change: 'Property Metadata Change',
};

export class HomeTitleAlertPipeline {
  private notificationService: NotificationService;
  private config: AlertPipelineConfig;
  private pendingDedup = new Map<string, number>();

  constructor(config?: Partial<AlertPipelineConfig>) {
    this.config = { ...DEFAULT_CONFIG, ...config };
    this.notificationService = new NotificationService(loadNotificationConfig());
  }

  async processChangeDetection(
    result: ChangeDetectionResult,
    subscriptionId: string,
    userId: string,
  ): Promise<PropertyAlert | null> {
    const severity = this.mapSeverity(result.severity);
    const shouldAlert = this.shouldAlert(result, severity);

    if (!shouldAlert) {
      return null;
    }

    const dedupKey = this.buildDedupKey(userId, result.propertyId, result.changeType);

    const isDuplicate = await this.checkDedup(dedupKey);
    if (isDuplicate) {
      return null;
    }

    const subscription = await prisma.subscription.findUnique({
      where: { id: subscriptionId },
      select: { tier: true },
    });

    if (!subscription) {
      return null;
    }

    const channels = this.getChannelsForTier(subscription.tier);
    const title = this.buildTitle(result);
    const message = this.buildMessage(result);

    const alert = await prisma.alert.create({
      data: {
        subscriptionId,
        userId,
        type: 'system_warning',
        title,
        message,
        severity: severity as AlertSeverity,
        channel: channels as AlertChannel[],
      },
    });

    await this.recordDedup(dedupKey);

    const propertyAlert: PropertyAlert = {
      id: alert.id,
      propertyId: result.propertyId,
      subscriptionId,
      userId,
      changeType: result.changeType,
      severity,
      title,
      message,
      changeDetectionResult: result,
      channel: channels,
      dedupKey,
      createdAt: alert.createdAt.toISOString(),
    };

    await this.createNormalizedAlert(result, userId, subscriptionId, alert.id, severity);

    if (subscription.tier === 'premium') {
      await this.dispatchNotification(propertyAlert, userId);
    }

    return propertyAlert;
  }

  async processBatch(
    results: ChangeDetectionResult[],
    subscriptionId: string,
    userId: string,
  ): Promise<PropertyAlert[]> {
    const alerts: PropertyAlert[] = [];

    for (const result of results) {
      const alert = await this.processChangeDetection(result, subscriptionId, userId);
      if (alert) {
        alerts.push(alert);
      }
    }

    if (alerts.length > 1) {
      await this.createCorrelationGroup(alerts, userId);
    }

    return alerts;
  }

  private shouldAlert(result: ChangeDetectionResult, severity: AlertSeverityLevel): boolean {
    const severityOrder: Severity[] = ['minor', 'moderate', 'major'];
    const minSeverityOrder: Severity[] = ['minor', 'moderate', 'major'];
    const resultIdx = severityOrder.indexOf(result.severity);
    const minIdx = minSeverityOrder.indexOf(this.config.minSeverity);

    return resultIdx >= minIdx && result.confidence >= 0.7;
  }

  private mapSeverity(severity: Severity): AlertSeverityLevel {
    return SEVERITY_MAP[severity] || 'info';
  }

  private buildDedupKey(userId: string, propertyId: string, changeType: ChangeType): string {
    return `hometitle:${userId}:${propertyId}:${changeType}`;
  }

  private async checkDedup(dedupKey: string): Promise<boolean> {
    const recentAlert = await prisma.alert.findFirst({
      where: {
        subscriptionId: dedupKey.split(':')[1] ? undefined : undefined,
        title: {
          contains: dedupKey.split(':')[2],
        },
        createdAt: {
          gte: new Date(Date.now() - this.config.dedupWindowMs),
        },
      },
      orderBy: { createdAt: 'desc' },
    });

    if (recentAlert) {
      return true;
    }

    const inMemoryExpiry = this.pendingDedup.get(dedupKey);
    if (inMemoryExpiry && Date.now() < inMemoryExpiry) {
      return true;
    }

    return false;
  }

  private async recordDedup(dedupKey: string): Promise<void> {
    this.pendingDedup.set(
      dedupKey,
      Date.now() + this.config.dedupWindowMs,
    );
  }

  private getChannelsForTier(tier: string): NotificationChannel[] {
    if (tier === 'premium') {
      return [...this.config.premiumTierChannels];
    }
    return [...this.config.defaultChannels];
  }

  private buildTitle(result: ChangeDetectionResult): string {
    const label = CHANGE_TYPE_LABELS[result.changeType] || 'Property Change';
    const severityUpper = result.severity.toUpperCase();
    return `[${severityUpper}] ${label} detected`;
  }

  private buildMessage(result: ChangeDetectionResult): string {
    const changes = result.changes
      .map(c => `- ${c.field}: ${String(c.oldValue)} → ${String(c.newValue)}`)
      .join('\n');

    return `Change detected on property ${result.propertyId}.\n\nChanges:\n${changes}\n\nConfidence: ${(result.confidence * 100).toFixed(1)}%`;
  }

  private async createNormalizedAlert(
    result: ChangeDetectionResult,
    userId: string,
    subscriptionId: string,
    sourceAlertId: string,
    severity: AlertSeverityLevel,
  ): Promise<void> {
    const normalizedSeverity = this.mapToNormalizedSeverity(severity);

    await prisma.normalizedAlert.create({
      data: {
        source: 'DARKWATCH',
        category: this.mapToAlertCategory(result.changeType),
        severity: normalizedSeverity,
        userId,
        title: this.buildTitle(result),
        description: this.buildMessage(result),
        entities: JSON.stringify({
          propertyId: result.propertyId,
          changeType: result.changeType,
          subscriptionId,
        }),
        sourceAlertId,
        payload: JSON.stringify({
          confidence: result.confidence,
          changes: result.changes,
          detectedAt: result.detectedAt,
        }),
        createdAt: new Date(result.detectedAt),
      },
    });
  }

  private async createCorrelationGroup(
    alerts: PropertyAlert[],
    userId: string,
  ): Promise<void> {
    const entities = JSON.stringify({
      propertyIds: [...new Set(alerts.map(a => a.propertyId))],
      changeTypes: [...new Set(alerts.map(a => a.changeType))],
    });

    const highestSeverity = alerts.reduce((max, alert) => {
      const order: AlertSeverityLevel[] = ['info', 'warning', 'critical'];
      return order.indexOf(alert.severity) > order.indexOf(max) ? alert.severity : max;
    }, 'info' as AlertSeverityLevel);

    const group = await prisma.correlationGroup.create({
      data: {
        userId,
        entities,
        highestSeverity: this.mapToNormalizedSeverity(highestSeverity),
        status: 'ACTIVE',
        alertCount: alerts.length,
        summary: `${alerts.length} property change alert${alerts.length > 1 ? 's' : ''} correlated`,
      },
    });

    await prisma.normalizedAlert.updateMany({
      where: {
        sourceAlertId: { in: alerts.map(a => a.id) },
      },
      data: {
        groupId: group.id,
      },
    });
  }

  private async dispatchNotification(
    alert: PropertyAlert,
    userId: string,
  ): Promise<void> {
    try {
      const user = await prisma.user.findUnique({
        where: { id: userId },
        select: { email: true, name: true },
      });

      if (!user?.email) {
        return;
      }

      const htmlMessage = `<p>${alert.message.replace(/\n/g, '<br>')}</p>
        <p><strong>Property:</strong> ${alert.propertyId}</p>
        <p><strong>Change Type:</strong> ${CHANGE_TYPE_LABELS[alert.changeType]}</p>
        <p><strong>Severity:</strong> ${alert.severity.toUpperCase()}</p>`;

      for (const channel of alert.channel) {
        switch (channel) {
          case 'email':
            await this.notificationService.send({
              channel: 'email',
              to: user.email,
              subject: alert.title,
              htmlBody: htmlMessage,
              textBody: alert.message,
            });
            break;
          case 'push':
            await this.notificationService.send({
              channel: 'push',
              userId,
              title: alert.title,
              body: alert.message.slice(0, 200),
            });
            break;
          case 'sms':
            await this.notificationService.send({
              channel: 'sms',
              to: user.email,
              body: `[ShieldAI] ${alert.title}: ${alert.message.slice(0, 140)}`,
            });
            break;
        }
      }
    } catch (error) {
      console.error('[HomeTitleAlertPipeline] Notification dispatch error:', error);
    }
  }

  private mapToNormalizedSeverity(severity: AlertSeverityLevel): string {
    const map: Record<AlertSeverityLevel, string> = {
      info: 'INFO',
      warning: 'WARNING',
      critical: 'CRITICAL',
    };
    return map[severity] || 'INFO';
  }

  private mapToAlertCategory(changeType: ChangeType): string {
    const map: Record<ChangeType, string> = {
      ownership_transfer: 'CALL_ANOMALY',
      deed_change: 'CALL_ANOMALY',
      lien_filing: 'CALL_ANOMALY',
      tax_change: 'CALL_EVENT',
      metadata_change: 'CALL_EVENT',
    };
    return map[changeType] || 'CALL_EVENT';
  }

  cleanupExpiredDedups(): number {
    const now = Date.now();
    let cleaned = 0;
    for (const [key, expiry] of this.pendingDedup) {
      if (now >= expiry) {
        this.pendingDedup.delete(key);
        cleaned++;
      }
    }
    return cleaned;
  }
}

export const homeTitleAlertPipeline = new HomeTitleAlertPipeline();
