import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { PrismaService } from '@chatapp/database';

export interface BlockActionPayload {
  type: 'block_actions';
  trigger_id?: string;
  user?: { id: string; name: string };
  channel?: { id: string; name: string };
  message?: { id: string; text?: string };
  actions?: Array<{
    action_id: string;
    block_id?: string;
    value?: string;
    type?: string;
    selected_option?: { value: string; text: string };
    selected_options?: Array<{ value: string; text: string }>;
  }>;
  api_app_id?: string;
}

export interface ViewSubmissionPayload {
  type: 'view_submission';
  trigger_id?: string;
  user?: { id: string; name: string };
  view?: {
    id: string;
    callback_id?: string;
    state?: {
      values?: Record<string, Record<string, { type: string; value: string; selected_date?: string; selected_time?: string }>>;
    };
    private_metadata?: string;
    hash?: string;
  };
  api_app_id?: string;
}

export interface ViewClosedPayload {
  type: 'view_closed';
  user?: { id: string; name: string };
  view?: { id: string; callback_id?: string };
  api_app_id?: string;
}

export interface ShortcutPayload {
  type: 'shortcut';
  callback_id: string;
  trigger_id: string;
  user?: { id: string; name: string };
  channel?: { id: string; name: string };
  message?: { id: string; text?: string };
  api_app_id?: string;
}

type AnyPayload = BlockActionPayload | ViewSubmissionPayload | ViewClosedPayload | ShortcutPayload;

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

  constructor(private readonly prisma: PrismaService) {}

  /**
   * Handler principal que enruta según el tipo de interacción.
   */
  async handleInteractivePayload(workspaceId: string, payload: AnyPayload) {
    switch (payload.type) {
      case 'block_actions':
        return this.handleBlockActions(workspaceId, payload as BlockActionPayload);
      case 'view_submission':
        return this.handleViewSubmission(workspaceId, payload as ViewSubmissionPayload);
      case 'view_closed':
        return this.handleViewClosed(workspaceId, payload as ViewClosedPayload);
      case 'shortcut':
        return this.handleShortcut(workspaceId, payload as ShortcutPayload);
      default:
        throw new BadRequestException(`Unknown interaction type: ${(payload as any).type}`);
    }
  }

  /**
   * Maneja block_actions: clicks en botones, selección de menús, etc.
   * Busca el handler configurado en el app o en la configuración del bloque.
   */
  async handleBlockActions(workspaceId: string, payload: BlockActionPayload) {
    const appId = payload.api_app_id;
    const actions = payload.actions || [];

    if (actions.length === 0) {
      return { ok: true, message: 'No actions in payload' };
    }

    const results: any[] = [];

    for (const action of actions) {
      const actionId = action.action_id;
      this.logger.log(`Block action received: ${actionId} from user ${payload.user?.id}`);

      // Buscar el app asociado para determinar el callback URL
      let callbackUrl: string | undefined;
      if (appId) {
        const app = await this.prisma.app.findFirst({
          where: { id: appId, workspaceId, isActive: true },
          select: { config: true },
        });
        if (app) {
          const config = app.config as any;
          callbackUrl = config?.action_urls?.[actionId] || config?.interactive_url;
        }
      }

      const result: any = {
        action_id: actionId,
        value: action.value,
        user: payload.user,
        channel: payload.channel,
        handled: false,
      };

      if (callbackUrl) {
        result.callback_url = callbackUrl;
        result.handled = true;
        this.logger.log(`Action ${actionId} routed to ${callbackUrl} (HTTP call skipped in dev)`);
      }

      results.push(result);
    }

    return { ok: true, type: 'block_actions', results };
  }

  /**
   * Maneja view_submission: envío de un modal/formulario.
   * Extrae los valores del state y los devuelve procesados.
   */
  async handleViewSubmission(workspaceId: string, payload: ViewSubmissionPayload) {
    const view = payload.view;
    if (!view) throw new BadRequestException('Missing view in view_submission payload');

    const callbackId = view.callback_id;
    const stateValues = view.state?.values || {};

    // Extraer los valores del formulario
    const extractedValues: Record<string, any> = {};
    for (const [blockId, blockValues] of Object.entries(stateValues)) {
      for (const [actionId, actionValue] of Object.entries(blockValues)) {
        extractedValues[actionId] = {
          value: actionValue.value,
          type: actionValue.type,
          selected_date: actionValue.selected_date,
          selected_time: actionValue.selected_time,
        };
      }
    }

    this.logger.log(`View submission for callback_id: ${callbackId}, user: ${payload.user?.id}`);

    // Buscar el app asociado
    let callbackUrl: string | undefined;
    if (payload.api_app_id) {
      const app = await this.prisma.app.findFirst({
        where: { id: payload.api_app_id, workspaceId, isActive: true },
        select: { config: true },
      });
      if (app) {
        const config = app.config as any;
        callbackUrl = config?.view_submission_url || config?.interactive_url;
      }
    }

    const result: any = {
      ok: true,
      type: 'view_submission',
      callback_id: callbackId,
      values: extractedValues,
      user: payload.user,
      private_metadata: view.private_metadata,
    };

    if (callbackUrl) {
      result.callback_url = callbackUrl;
      result.handled = true;
      this.logger.log(`View submission routed to ${callbackUrl} (HTTP call skipped in dev)`);
    }

    // Por defecto cerramos el modal
    result.response_action = 'clear';

    return result;
  }

  /**
   * Maneja view_closed: cuando el usuario cierra un modal sin enviar.
   */
  async handleViewClosed(_workspaceId: string, payload: ViewClosedPayload) {
    this.logger.log(`View closed: callback_id=${payload.view?.callback_id}, user=${payload.user?.id}`);
    return { ok: true, type: 'view_closed', callback_id: payload.view?.callback_id };
  }

  /**
   * Maneja shortcuts: invocaciones rápidas desde el menú.
   * Abre un modal con el trigger_id proporcionado.
   */
  async handleShortcut(workspaceId: string, payload: ShortcutPayload) {
    const callbackId = payload.callback_id;
    const triggerId = payload.trigger_id;

    this.logger.log(`Shortcut invoked: ${callbackId}, trigger_id: ${triggerId}`);

    // Buscar configuración del shortcut en los apps del workspace
    let modalConfig: any = null;
    if (payload.api_app_id) {
      const app = await this.prisma.app.findFirst({
        where: { id: payload.api_app_id, workspaceId, isActive: true },
        select: { config: true },
      });
      if (app) {
        const config = app.config as any;
        modalConfig = config?.shortcuts?.[callbackId];
      }
    }

    return {
      ok: true,
      type: 'shortcut',
      callback_id: callbackId,
      trigger_id: triggerId,
      user: payload.user,
      channel: payload.channel,
      modal: modalConfig || null,
      message: modalConfig ? 'Modal configuration found' : 'No modal configuration found for this shortcut',
    };
  }

  /**
   * Abre un modal interactivo usando el trigger_id.
   * En un entorno real, esto haría una llamada a la API con el trigger_id.
   */
  async openModal(workspaceId: string, triggerId: string, view: any) {
    this.logger.log(`Opening modal with trigger_id: ${triggerId}`);
    return {
      ok: true,
      trigger_id: triggerId,
      view,
      message: 'Modal open request processed (simulated)',
    };
  }

  /**
   * Actualiza un modal ya abierto.
   */
  async updateModal(workspaceId: string, externalId: string, view: any) {
    this.logger.log(`Updating modal with external_id: ${externalId}`);
    return {
      ok: true,
      external_id: externalId,
      view,
      message: 'Modal update request processed (simulated)',
    };
  }

  /**
   * Publica un mensaje efímero a un usuario en un canal.
   */
  async postEphemeral(
    workspaceId: string,
    data: { channel_id: string; user_id: string; text?: string; blocks?: any[] },
  ) {
    const channel = await this.prisma.channel.findFirst({
      where: { id: data.channel_id, workspaceId },
    });
    if (!channel) throw new NotFoundException('Channel not found');

    const message = await this.prisma.message.create({
      data: {
        workspaceId,
        channelId: data.channel_id,
        text: data.text || '',
        blocks: data.blocks || undefined,
        isSystem: true,
      },
    });

    return { ok: true, message_id: message.id, ephemeral: true, user_id: data.user_id };
  }
}