/**
 * Inbound support ticket creation — crm-support-center.
 *
 * Called from the comms.inbound queue consumer when autoCreateTickets is enabled.
 */
import {
  createDb,
  findCustomerContactByEmail,
  findTicketByThread,
  createTicket,
  createTicketMessage,
  autoReopenOnCustomerReply,
  getSlaEnabled,
  computeAndSetDueAt,
} from '@zync/db/queries'
import type { TicketObject } from '@zync/db/queries'
import { publishRealtimeEvent } from '@zync/realtime/server'
import type { Env, InboundMessage } from '@zync/types'
import { sanitizeCommentHtml } from '../lib/sanitize-comment'
import { enqueueTicketWebhook } from '../lib/ticket-webhooks'

export type InboundTicketChannel = 'email' | 'telegram' | 'whatsapp' | 'slack'

function normalizeMessageId(raw: string): string {
  return raw.replace(/^<|>$/g, '').trim()
}

function resolveEmailThreadId(msg: InboundMessage): string | null {
  const inReplyTo = msg.metadata['inReplyTo']
  if (typeof inReplyTo === 'string' && inReplyTo.trim()) {
    return normalizeMessageId(inReplyTo)
  }
  const references = msg.metadata['references']
  if (typeof references === 'string' && references.trim()) {
    const firstRef = references.split(/\s+/)[0]
    if (firstRef) return normalizeMessageId(firstRef)
  }
  return null
}

function mapTicketSource(channel: InboundTicketChannel): TicketObject['source'] {
  switch (channel) {
    case 'email':
      return 'email'
    case 'telegram':
      return 'telegram'
    case 'whatsapp':
      return 'whatsapp'
    case 'slack':
      return 'web'
  }
}

function mapMessageSource(channel: InboundTicketChannel): 'email' | 'telegram' | 'whatsapp' | 'web' {
  switch (channel) {
    case 'email':
      return 'email'
    case 'telegram':
      return 'telegram'
    case 'whatsapp':
      return 'whatsapp'
    case 'slack':
      return 'web'
  }
}

function resolveExternalThreadId(channel: InboundTicketChannel, msg: InboundMessage): string {
  if (channel === 'email') {
    const messageId = msg.metadata['messageId']
    if (typeof messageId === 'string' && messageId.trim()) {
      return normalizeMessageId(messageId)
    }
    return msg.from
  }
  if (channel === 'whatsapp') {
    return msg.from
  }
  return msg.chatId
}

function resolveAuthorName(channel: InboundTicketChannel, msg: InboundMessage): string | null {
  if (channel === 'telegram') {
    const firstName = msg.metadata['firstName']
    if (typeof firstName === 'string' && firstName.trim()) return firstName
    const username = msg.metadata['username']
    if (typeof username === 'string' && username.trim()) return `@${username}`
  }
  if (channel === 'email') {
    const fromName = msg.metadata['fromName']
    if (typeof fromName === 'string' && fromName.trim()) return fromName
  }
  return msg.from || null
}

function resolveTitle(channel: InboundTicketChannel, msg: InboundMessage, content: string): string {
  if (channel === 'email') {
    const subject = msg.metadata['subject']
    if (typeof subject === 'string' && subject.trim()) {
      return subject.replace(/^Re:\s*/i, '').trim().slice(0, 500)
    }
  }
  const firstLine = content.split('\n')[0]?.trim()
  if (firstLine) return firstLine.slice(0, 500)
  return `Support request from ${msg.from}`
}

async function emitTicketSideEffects(
  env: Env,
  tenantId: string,
  ticket: TicketObject,
  messageId: string,
  created: boolean,
  authorName: string | null,
  preview: string,
): Promise<void> {
  const event = created ? 'ticket.created' : 'ticket.replied'
  await enqueueTicketWebhook(env, tenantId, event, {
    ticketId: ticket.id,
    customerId: ticket.customer_id,
    source: ticket.source,
    priority: ticket.priority,
    assigneeId: ticket.assignee_id,
    messageId,
    authorType: 'customer',
    sourceChannel: ticket.source,
  })

  try {
    await publishRealtimeEvent(env.REALTIME_QUEUE, {
      type: 'ticket.message_added',
      tenantId,
      payload: {
        ticketId: ticket.id,
        messageId,
        authorId: '',
        authorName: authorName ?? 'Customer',
        preview: preview.slice(0, 200),
      },
    })
  } catch {
    // Non-fatal
  }
}

export async function createSupportTicketFromInbound(
  env: Env,
  tenantId: string,
  channel: InboundTicketChannel,
  msg: InboundMessage,
): Promise<{ ticketId: string }> {
  const db = createDb(env)
  const ticketSource = mapTicketSource(channel)
  const messageSource = mapMessageSource(channel)
  const sanitizedContent = sanitizeCommentHtml(msg.text || '')
  const authorName = resolveAuthorName(channel, msg)

  let contactMatch: { id: string; customerId: string } | null = null
  if (channel === 'email') {
    const contact = await findCustomerContactByEmail(db, tenantId, msg.from.toLowerCase())
    if (contact) {
      contactMatch = { id: contact.id, customerId: contact.customerId }
    }
  }

  const threadLookupId =
    channel === 'email' ? resolveEmailThreadId(msg) : resolveExternalThreadId(channel, msg)

  let existing: TicketObject | null = null
  if (threadLookupId) {
    existing = await findTicketByThread(db, tenantId, ticketSource, threadLookupId)
  }

  if (existing) {
    const message = await createTicketMessage(db, tenantId, existing.id, {
      author_type: 'customer',
      author_name: authorName,
      content: sanitizedContent,
      source: messageSource,
    })
    await autoReopenOnCustomerReply(db, tenantId, existing)
    await emitTicketSideEffects(
      env,
      tenantId,
      existing,
      message.id,
      false,
      authorName,
      sanitizedContent,
    )
    return { ticketId: existing.id }
  }

  const externalThreadId = resolveExternalThreadId(channel, msg)
  const externalId =
    channel === 'email'
      ? (typeof msg.metadata['messageId'] === 'string'
          ? normalizeMessageId(msg.metadata['messageId'])
          : null)
      : channel === 'telegram'
        ? String(msg.metadata['messageId'] ?? '')
        : typeof msg.metadata['waMessageId'] === 'string'
          ? msg.metadata['waMessageId']
          : null

  const ticket = await createTicket(db, tenantId, {
    title: resolveTitle(channel, msg, sanitizedContent),
    description: sanitizedContent,
    priority: 'medium',
    source: ticketSource,
    customer_id: contactMatch?.customerId ?? null,
    contact_id: contactMatch?.id ?? null,
    external_id: externalId,
    external_thread_id: externalThreadId,
  })

  if (await getSlaEnabled(db, tenantId)) {
    await computeAndSetDueAt(
      db,
      tenantId,
      ticket.id,
      ticket.priority,
      new Date(ticket.created_at),
    )
  }

  const message = await createTicketMessage(db, tenantId, ticket.id, {
    author_type: 'customer',
    author_name: authorName,
    content: sanitizedContent,
    source: messageSource,
  })

  await emitTicketSideEffects(
    env,
    tenantId,
    ticket,
    message.id,
    true,
    authorName,
    sanitizedContent,
  )

  return { ticketId: ticket.id }
}

export function bindCreateSupportTicket(
  env: Env,
  tenantId: string,
  channel: InboundTicketChannel,
): (msg: InboundMessage) => Promise<{ ticketId: string }> {
  return (msg) => createSupportTicketFromInbound(env, tenantId, channel, msg)
}
