/**
 * Conversation-specific workflow actions
 *
 * These actions provide safe, specialized operations for conversation data in workflows.
 * They replace generic database operations with purpose-built functions that:
 * - Use Convex indexes for efficient queries
 * - Require organizationId or conversationId to prevent accidental bulk operations
 * - Use lodash for safe nested metadata updates
 * - Support flexible filtering on status, priority, and metadata fields
 * - Follow Convex best practices
 */

import { v } from 'convex/values';

import type { Id } from '../../../_generated/dataModel';
import { createDebugLog } from '../../../lib/debug_log';
import {
  jsonRecordValidator,
  jsonValueValidator,
} from '../../../lib/shared/schemas/utils/json_value';
import type { ActionDefinition } from '../../helpers/nodes/action/types';
import type { ConversationStatus, ConversationPriority } from './helpers/types';

const debugLog = createDebugLog('DEBUG_WORKFLOW', '[Conversations]');
import { createConversation } from './helpers/create_conversation';
import { createConversationFromEmail } from './helpers/create_conversation_from_email';
import { createConversationFromSentEmail } from './helpers/create_conversation_from_sent_email';
import {
  getEmailSyncCursor,
  updateEmailSyncCursor,
} from './helpers/email_sync_cursor';
import { queryConversationMessages } from './helpers/query_conversation_messages';
import { queryLatestMessageByDeliveryState } from './helpers/query_latest_message_by_delivery_state';
import { updateConversations } from './helpers/update_conversations';

// Common field validators
const paginationOptsValidator = v.object({
  numItems: v.number(),
  cursor: v.union(v.string(), v.null()),
});

const directionValidator = v.union(v.literal('inbound'), v.literal('outbound'));

const deliveryStateValidator = v.union(
  v.literal('queued'),
  v.literal('sent'),
  v.literal('delivered'),
  v.literal('failed'),
);

// Status validator matching ConversationStatus type
const statusValidator = v.union(
  v.literal('open'),
  v.literal('closed'),
  v.literal('archived'),
  v.literal('spam'),
);

// Priority validator matching ConversationPriority type
const priorityValidator = v.union(
  v.literal('low'),
  v.literal('medium'),
  v.literal('high'),
  v.literal('urgent'),
);

// Type for conversation operation params (discriminated union)
// Note: This type is maintained separately from the parametersValidator for clarity.
// The TypeScript type provides IDE support, while the validator provides runtime validation.
type ConversationActionParams =
  | {
      operation: 'create';
      customerId?: Id<'customers'>;
      subject?: string;
      status?: ConversationStatus;
      priority?: ConversationPriority;
      type?: string;
      channel?: string;
      direction?: 'inbound' | 'outbound';
      metadata?: Record<string, unknown>;
    }
  | {
      operation: 'get_by_id';
      conversationId: Id<'conversations'>;
    }
  | {
      operation: 'query_messages';
      paginationOpts: { numItems: number; cursor: string | null };
      conversationId?: Id<'conversations'>;
      channel?: string;
      direction?: 'inbound' | 'outbound';
    }
  | {
      operation: 'query_latest_message_by_delivery_state';
      channel: string;
      direction: 'inbound' | 'outbound';
      deliveryState: 'queued' | 'sent' | 'delivered' | 'failed';
      integrationName?: string;
    }
  | {
      operation: 'update';
      conversationId: Id<'conversations'>;
      updates: Record<string, unknown>;
    }
  | {
      operation: 'create_from_email';
      emails: unknown;
      accountEmail?: string;
      status?: ConversationStatus;
      priority?: ConversationPriority;
      type?: string;
      integrationName?: string;
    }
  | {
      operation: 'create_from_sent_email';
      emails: unknown;
      accountEmail?: string;
      status?: ConversationStatus;
      priority?: ConversationPriority;
      type?: string;
      integrationName?: string;
    }
  | {
      operation: 'get_email_sync_cursor';
      integrationName: string;
    }
  | {
      operation: 'update_email_sync_cursor';
      integrationName: string;
      cursor: Record<string, unknown>;
    };

export const conversationAction: ActionDefinition<ConversationActionParams> = {
  type: 'conversation',
  title: 'Conversation Operation',
  description: `Execute conversation-specific operations (create, get_by_id, query_messages, query_latest_message_by_delivery_state, update, create_from_email, create_from_sent_email, get_email_sync_cursor, update_email_sync_cursor). organizationId is automatically read from workflow context variables.

FOR EMAIL WORKFLOWS:
When creating outbound email conversations, include these fields in the metadata object:
- emailSubject: Subject line (required)
- emailBody: HTML or Markdown body content (required)
- customerEmail: Recipient email address (required)
- emailPreview: Preview text for inbox (optional)
- emailCc: CC recipients (optional)
- emailBcc: BCC recipients (optional)

Then create an approval with resourceType: 'conversations' to trigger sending when approved.
`,
  parametersValidator: v.union(
    // create: Create a new conversation
    v.object({
      operation: v.literal('create'),
      customerId: v.optional(v.id('customers')),
      subject: v.optional(v.string()),
      status: v.optional(statusValidator),
      priority: v.optional(priorityValidator),
      type: v.optional(v.string()),
      channel: v.optional(v.string()),
      direction: v.optional(directionValidator),
      metadata: v.optional(jsonRecordValidator),
    }),
    // get_by_id: Get a conversation by ID
    v.object({
      operation: v.literal('get_by_id'),
      conversationId: v.id('conversations'),
    }),
    // query_messages: Query conversation messages with pagination
    v.object({
      operation: v.literal('query_messages'),
      paginationOpts: paginationOptsValidator,
      conversationId: v.optional(v.id('conversations')),
      channel: v.optional(v.string()),
      direction: v.optional(directionValidator),
    }),
    // query_latest_message_by_delivery_state: Query latest message by delivery state
    v.object({
      operation: v.literal('query_latest_message_by_delivery_state'),
      channel: v.string(),
      direction: directionValidator,
      deliveryState: deliveryStateValidator,
      integrationName: v.optional(v.string()),
    }),
    // update: Update a conversation by ID
    v.object({
      operation: v.literal('update'),
      conversationId: v.id('conversations'),
      updates: v.record(v.string(), jsonValueValidator),
    }),
    // create_from_email: Create conversation from email (supports both inbound and outbound via accountEmail)
    v.object({
      operation: v.literal('create_from_email'),
      emails: jsonValueValidator,
      accountEmail: v.optional(v.string()),
      status: v.optional(statusValidator),
      priority: v.optional(priorityValidator),
      type: v.optional(v.string()),
      integrationName: v.optional(v.string()),
    }),
    // create_from_sent_email: Create conversation from sent email
    v.object({
      operation: v.literal('create_from_sent_email'),
      emails: jsonValueValidator,
      accountEmail: v.optional(v.string()),
      status: v.optional(statusValidator),
      priority: v.optional(priorityValidator),
      type: v.optional(v.string()),
      integrationName: v.optional(v.string()),
    }),
    // get_email_sync_cursor: Read provider-opaque sync cursor from integration metadata
    v.object({
      operation: v.literal('get_email_sync_cursor'),
      integrationName: v.string(),
    }),
    // update_email_sync_cursor: Write provider-opaque sync cursor to integration metadata
    v.object({
      operation: v.literal('update_email_sync_cursor'),
      integrationName: v.string(),
      cursor: jsonRecordValidator,
    }),
  ),
  async execute(ctx, params, variables) {
    // Read and validate organizationId from workflow context variables
    const organizationId = variables?.organizationId;

    if (typeof organizationId !== 'string' || !organizationId) {
      throw new Error(
        'conversation action requires a non-empty string organizationId in workflow context',
      );
    }

    switch (params.operation) {
      case 'create': {
        return await createConversation(ctx, {
          organizationId,
          customerId: params.customerId,
          subject: params.subject,
          status: params.status,
          priority: params.priority,
          type: params.type,
          channel: params.channel,
          direction: params.direction,
          metadata: params.metadata,
        });
      }

      case 'get_by_id': {
        const { internal } = await import('../../../_generated/api');
        return await ctx.runQuery(
          internal.conversations.internal_queries.getConversationById,
          {
            conversationId: params.conversationId,
          },
        );
      }

      case 'query_messages': {
        return await queryConversationMessages(ctx, {
          organizationId,
          conversationId: params.conversationId,
          channel: params.channel,
          direction: params.direction,
          paginationOpts: params.paginationOpts, // Required by validator
        });
      }

      case 'query_latest_message_by_delivery_state': {
        return await queryLatestMessageByDeliveryState(ctx, {
          organizationId,
          channel: params.channel,
          direction: params.direction,
          deliveryState: params.deliveryState,
          integrationName: params.integrationName,
        });
      }

      case 'update': {
        return await updateConversations(ctx, {
          organizationId, // For organization ownership validation
          conversationId: params.conversationId, // Required by validator
          updates: params.updates, // Required by validator
        });
      }

      case 'create_from_email': {
        const accountEmail = params.accountEmail?.trim() || undefined;
        return await createConversationFromEmail(ctx, {
          organizationId,
          emails: params.emails,
          status: params.status,
          priority: params.priority,
          type: params.type,
          accountEmail,
          integrationName: params.integrationName,
        });
      }

      case 'create_from_sent_email': {
        const accountEmail = params.accountEmail?.trim() || undefined;
        return await createConversationFromSentEmail(ctx, {
          organizationId,
          emails: params.emails,
          status: params.status,
          priority: params.priority,
          accountEmail,
          type: params.type,
          integrationName: params.integrationName,
        });
      }

      case 'get_email_sync_cursor': {
        return await getEmailSyncCursor(ctx, {
          organizationId,
          integrationName: params.integrationName,
        });
      }

      case 'update_email_sync_cursor': {
        debugLog('update_email_sync_cursor cursor:', {
          cursor: params.cursor,
          cursorType: typeof params.cursor,
          integrationName: params.integrationName,
        });
        await updateEmailSyncCursor(ctx, {
          organizationId,
          integrationName: params.integrationName,
          cursor: params.cursor,
        });
        return { success: true };
      }

      default:
        throw new Error(
          `Unsupported conversation operation: ${(params as { operation: string }).operation}`,
        );
    }
  },
};
