import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { PrismaService } from '@chatapp/database';
import { ConfigService } from '@nestjs/config';
import * as crypto from 'crypto';

export interface OutgoingWebhookConfig {
  url: string;
  eventTypes: string[];
  isActive: boolean;
  secret: string;
}

@Injectable()
export class OutgoingWebhooksService {
  private readonly logger = new Logger(OutgoingWebhooksService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly config: ConfigService,
  ) {}

  /**
   * Lista los outgoing webhooks (event subscriptions) de un workspace.
   * Los outgoing webhooks usan el modelo EventSubscription.
   */
  async listOutgoingWebhooks(workspaceId: string) {
    return this.prisma.eventSubscription.findMany({
      where: { workspaceId },
      include: {
        app: { select: { id: true, appName: true, appSlug: true } },
      },
      orderBy: { createdAt: 'desc' },
    });
  }

  /**
   * Crea un nuevo outgoing webhook (event subscription).
   */
  async createOutgoingWebhook(
    workspaceId: string,
    data: { callback_url: string; event_types: string[]; app_id?: string },
  ) {
    if (!data.callback_url) throw new BadRequestException('callback_url is required');
    if (!data.event_types || data.event_types.length === 0) {
      throw new BadRequestException('event_types must have at least one event');
    }

    // Validar app_id si se especifica
    if (data.app_id) {
      const app = await this.prisma.app.findFirst({
        where: { id: data.app_id, workspaceId, isActive: true },
      });
      if (!app) throw new BadRequestException('App not found or not installed');
    }

    const secret = crypto.randomBytes(32).toString('hex');

    return this.prisma.eventSubscription.create({
      data: {
        workspaceId,
        appId: data.app_id || null,
        callbackUrl: data.callback_url,
        eventTypes: data.event_types,
        secret,
        isActive: true,
      },
      include: {
        app: { select: { id: true, appName: true, appSlug: true } },
      },
    });
  }

  /**
   * Obtiene un outgoing webhook específico.
   */
  async getOutgoingWebhook(workspaceId: string, webhookId: string) {
    const webhook = await this.prisma.eventSubscription.findUnique({
      where: { id: webhookId },
      include: {
        app: { select: { id: true, appName: true, appSlug: true } },
      },
    });
    if (!webhook || webhook.workspaceId !== workspaceId) {
      throw new NotFoundException('Outgoing webhook not found');
    }
    return webhook;
  }

  /**
   * Actualiza un outgoing webhook.
   */
  async updateOutgoingWebhook(
    workspaceId: string,
    webhookId: string,
    data: { callback_url?: string; event_types?: string[] },
  ) {
    const webhook = await this.prisma.eventSubscription.findFirst({
      where: { id: webhookId, workspaceId },
    });
    if (!webhook) throw new NotFoundException('Outgoing webhook not found');

    const updateData: any = {};
    if (data.callback_url !== undefined) updateData.callbackUrl = data.callback_url;
    if (data.event_types !== undefined) updateData.eventTypes = data.event_types;

    return this.prisma.eventSubscription.update({
      where: { id: webhookId },
      data: updateData,
      include: {
        app: { select: { id: true, appName: true, appSlug: true } },
      },
    });
  }

  /**
   * Activa un outgoing webhook.
   */
  async activateOutgoingWebhook(workspaceId: string, webhookId: string) {
    const webhook = await this.prisma.eventSubscription.findFirst({
      where: { id: webhookId, workspaceId },
    });
    if (!webhook) throw new NotFoundException('Outgoing webhook not found');

    return this.prisma.eventSubscription.update({
      where: { id: webhookId },
      data: { isActive: true },
      include: {
        app: { select: { id: true, appName: true, appSlug: true } },
      },
    });
  }

  /**
   * Desactiva un outgoing webhook.
   */
  async deactivateOutgoingWebhook(workspaceId: string, webhookId: string) {
    const webhook = await this.prisma.eventSubscription.findFirst({
      where: { id: webhookId, workspaceId },
    });
    if (!webhook) throw new NotFoundException('Outgoing webhook not found');

    return this.prisma.eventSubscription.update({
      where: { id: webhookId },
      data: { isActive: false },
      include: {
        app: { select: { id: true, appName: true, appSlug: true } },
      },
    });
  }

  /**
   * Elimina un outgoing webhook.
   */
  async deleteOutgoingWebhook(workspaceId: string, webhookId: string) {
    const webhook = await this.prisma.eventSubscription.findFirst({
      where: { id: webhookId, workspaceId },
    });
    if (!webhook) throw new NotFoundException('Outgoing webhook not found');

    await this.prisma.eventSubscription.delete({ where: { id: webhookId } });
    return { deleted: true, id: webhookId };
  }

  /**
   * Envía un evento a todos los outgoing webhooks suscritos a ese tipo de evento.
   * Este es el método que se llama desde otros servicios cuando ocurre un evento.
   */
  async dispatchEvent(workspaceId: string, eventType: string, eventData: any) {
    const webhooks = await this.prisma.eventSubscription.findMany({
      where: {
        workspaceId,
        isActive: true,
        eventTypes: { has: eventType },
      },
    });

    if (webhooks.length === 0) return { dispatched: 0 };

    const results: any[] = [];

    for (const webhook of webhooks) {
      const payload = {
        event: eventType,
        data: eventData,
        workspace_id: workspaceId,
        timestamp: new Date().toISOString(),
      };

      // Firmar el payload con el secreto del webhook (HMAC SHA256)
      const signature = crypto
        .createHmac('sha256', webhook.secret)
        .update(JSON.stringify(payload))
        .digest('hex');

      // En un entorno real, haríamos fetch al webhook.callbackUrl
      // con headers: X-Webhook-Signature, Content-Type: application/json
      this.logger.log(
        `Dispatching event ${eventType} to ${webhook.callbackUrl} (signature: ${signature.substring(0, 16)}...)`,
      );

      results.push({
        webhook_id: webhook.id,
        callback_url: webhook.callbackUrl,
        event_type: eventType,
        delivered: true,
        signature: signature.substring(0, 16) + '...',
        // En producción: status, response_time, etc.
      });
    }

    return { dispatched: results.length, results };
  }

  /**
   * Prueba un outgoing webhook enviando un evento de prueba.
   */
  async testOutgoingWebhook(workspaceId: string, webhookId: string) {
    const webhook = await this.prisma.eventSubscription.findFirst({
      where: { id: webhookId, workspaceId },
    });
    if (!webhook) throw new NotFoundException('Outgoing webhook not found');

    const testPayload = {
      event: 'webhook.test',
      data: {
        test: true,
        message: 'Test webhook delivery',
        webhook_id: webhookId,
      },
      workspace_id: workspaceId,
      timestamp: new Date().toISOString(),
    };

    const signature = crypto
      .createHmac('sha256', webhook.secret)
      .update(JSON.stringify(testPayload))
      .digest('hex');

    this.logger.log(`Test webhook sent to ${webhook.callbackUrl}`);

    return {
      ok: true,
      webhook_id: webhookId,
      callback_url: webhook.callbackUrl,
      payload: testPayload,
      signature: signature,
      message: 'Test webhook delivery processed (simulated)',
    };
  }
}