/**
 * Shared contracts for the passenger in-app customer-support chat.
 *
 * Unlike freight (company-scoped), a passenger conversation ("ticket") belongs to a
 * single **individual passenger** (keyed by their IAM user id). Backoffice agents
 * work a shared inbox (no assignment). Messages are text-only for the MVP.
 *
 * These are the wire (JSON) shapes — dates are ISO strings — plus the frozen
 * Socket.IO event/namespace constants shared by the gateway (emitter) and both
 * passenger web apps (subscribers).
 */

/** Lifecycle of a support conversation. Mirrors the Prisma `SupportConversationStatus`. */
export enum PassengerSupportStatus {
  OPEN = "OPEN",
  RESOLVED = "RESOLVED",
  CLOSED = "CLOSED",
}

/** Author of a message. The Prisma `SupportSender` also has `BOT` (legacy, unused here). */
export enum PassengerSupportSender {
  USER = "USER",
  AGENT = "AGENT",
}

/**
 * A file attached to a passenger support message.
 *
 * Structurally identical to the freight `SupportAttachmentDto` — kept as its own
 * declaration because the two namespaces are independently versioned and the
 * backing stores differ (Prisma `SupportAttachment` here, polymorphic
 * `FileRecord` in freight). The upload rules themselves are shared: see
 * `SUPPORT_ATTACHMENT_*` in `common/support-attachments`.
 */
export interface PassengerSupportAttachmentDto {
  id: string;
  name: string;
  mimeType: string;
  /** Bytes. */
  size: number;
  /**
   * Short-lived signed URL — serves both inline preview and download. Expires
   * (see SUPPORT_ATTACHMENT_URL_TTL_SECONDS); clients must not persist it,
   * refetch the thread to renew.
   *
   * There is no API-streamed alternative here, unlike freight: this app has no
   * general file endpoint, so the signed URL is the only handle. It is minted
   * only into responses the caller was already authorized to receive.
   */
  url: string;
}

/** A single chat message on the wire. */
export interface PassengerSupportMessageDto {
  id: string;
  conversationId: string;
  sender: PassengerSupportSender;
  /** Display name of the author, best-effort. */
  authorName?: string | null;
  /** Empty string for attachment-only messages. */
  text: string;
  attachments: PassengerSupportAttachmentDto[];
  createdAt: string;
}

/** A conversation ("ticket") on the wire, with denormalized last-message fields. */
export interface PassengerSupportConversationDto {
  id: string;
  /** Set for authenticated passengers; null for guest conversations. */
  userId?: string | null;
  /** Set for guest (unauthenticated) conversations. */
  guestId?: string | null;
  guestEmail?: string | null;
  guestPhone?: string | null;
  passengerId?: string | null;
  /** Display name — passenger's name for authed, guest's name for guests. */
  passengerName?: string | null;
  subject?: string | null;
  status: PassengerSupportStatus;
  assignedAgentId?: string | null;
  lastMessageAt?: string | null;
  lastMessagePreview?: string | null;
  lastMessageSender?: PassengerSupportSender | null;
  /** Unread count for the caller's side (messages from the other sender after their cursor). */
  unreadCount: number;
  createdAt: string;
  updatedAt: string;
}

/** Customer opens a new ticket: subject + first message. */
export interface CreatePassengerSupportConversationDto {
  subject: string;
  initialMessage: string;
}

/** Guest (unauthenticated) opens a ticket: identity + contact captured up front. */
export interface CreateGuestSupportConversationDto {
  guestId: string;
  name: string;
  email: string;
  phone?: string;
  subject: string;
  initialMessage: string;
}

/**
 * Post a message into an existing conversation.
 *
 * As in freight, attachments travel as `multipart/form-data` (a `text` field
 * plus `attachments` file parts) rather than as ids in this body, so files are
 * persisted against the message that owns them in one request.
 */
export interface SendPassengerSupportMessageDto {
  /** Optional only when the request carries at least one attachment. */
  text?: string;
}

/**
 * The single device-scoped thread for the portal: conversation + its newest page
 * of messages.
 *
 * `messages` is the *first page only* (newest N, oldest-first within the page) —
 * it is not the whole thread. Page backwards with `nextCursor` via the messages
 * endpoint, exactly as the backoffice does.
 */
export interface PassengerSupportThreadDto {
  conversation: PassengerSupportConversationDto | null;
  messages: PassengerSupportMessageDto[];
  /** Cursor for the next (older) page; null when the thread's start is loaded. */
  nextCursor: string | null;
}

/**
 * A page of messages, walking backwards from newest. Keyset — not offset — so a
 * message arriving while the reader is scrolled back cannot shift or duplicate
 * pages already fetched. See the freight twin for the full rationale.
 */
export interface PassengerSupportMessageListResult {
  /** Oldest-first within the page. */
  items: PassengerSupportMessageDto[];
  /** Pass back as `before`; null at the start of the thread. */
  nextCursor: string | null;
}

/** Paginated list envelope for the conversations list endpoints. */
export interface PassengerSupportConversationListResult {
  items: PassengerSupportConversationDto[];
  count: number;
  /** Total unread conversations for the caller's side (badge source). */
  unreadCount: number;
}

/** Socket.io event names pushed server → client on the passenger support namespace. */
export const PASSENGER_SUPPORT_WS_EVENTS = {
  MESSAGE_NEW: "passenger-support:message-new",
  CONVERSATION_UPDATED: "passenger-support:conversation-updated",
} as const;

/** Socket.io namespace the passenger support gateway listens on. */
export const PASSENGER_SUPPORT_WS_NAMESPACE = "passenger-support-chat";

/** Payload for {@link PASSENGER_SUPPORT_WS_EVENTS.MESSAGE_NEW}. */
export interface PassengerSupportMessageEvent {
  conversation: PassengerSupportConversationDto;
  message: PassengerSupportMessageDto;
}
