/**
 * Support Tickets Service
 *
 * Provides functionality for managing customer support tickets,
 * responses, assignments, and metrics.
 */

import prisma from '@/lib/db';
import { nanoid } from 'nanoid';

export type TicketCategory = 'BILLING' | 'TECHNICAL' | 'FEATURE_REQUEST' | 'OTHER';
export type TicketPriority = 'LOW' | 'NORMAL' | 'URGENT';
export type TicketStatus = 'OPEN' | 'IN_PROGRESS' | 'RESOLVED' | 'CLOSED';
export type TicketSenderType = 'USER' | 'STAFF';

export interface Ticket {
  id: string;
  userId: string;
  subject: string;
  category: TicketCategory;
  priority: TicketPriority;
  message: string;
  status: TicketStatus;
  attachments: string[] | null;
  resolvedAt: Date | null;
  resolvedBy: string | null;
  resolution: string | null;
  createdAt: Date;
  updatedAt: Date;
  user: {
    id: string;
    email: string;
    name: string | null;
    companyName: string | null;
  };
  assignedStaff?: {
    id: string;
    name: string;
  } | null;
}

export interface TicketFilters {
  status?: TicketStatus;
  priority?: TicketPriority;
  category?: TicketCategory;
  assignedTo?: string;
  search?: string;
  page?: number;
  limit?: number;
}

export interface TicketMetrics {
  totalOpen: number;
  totalInProgress: number;
  totalResolved: number;
  totalClosed: number;
  avgResolutionTimeHours: number;
  ticketsByCategory: Record<TicketCategory, number>;
  ticketsByPriority: Record<TicketPriority, number>;
  urgentTickets: number;
}

export interface TicketMessage {
  id: string;
  ticketId: string;
  senderId: string;
  senderType: TicketSenderType;
  message: string;
  attachments: string[] | null;
  isRead: boolean;
  readAt: Date | null;
  createdAt: Date;
  senderName?: string;
  senderEmail?: string;
}

export interface CreateTicketRequest {
  userId: string;
  subject: string;
  category: TicketCategory;
  priority?: TicketPriority;
  message: string;
  attachments?: string[];
}

export interface TicketWithMessages extends Ticket {
  messageThread: TicketMessage[];
  unreadCount?: number;
}

/**
 * Get tickets with filtering and pagination
 */
export async function getTickets(filters: TicketFilters): Promise<{
  tickets: Ticket[];
  total: number;
  page: number;
  totalPages: number;
}> {
  const page = filters.page || 1;
  const limit = filters.limit || 25;
  const skip = (page - 1) * limit;

  const where: Record<string, unknown> = {};

  if (filters.status) {
    where.status = filters.status;
  }

  if (filters.priority) {
    where.priority = filters.priority;
  }

  if (filters.category) {
    where.category = filters.category;
  }

  if (filters.search) {
    where.OR = [
      { subject: { contains: filters.search, mode: 'insensitive' } },
      { message: { contains: filters.search, mode: 'insensitive' } },
      { id: { contains: filters.search, mode: 'insensitive' } },
      { users: { email: { contains: filters.search, mode: 'insensitive' } } },
      { users: { name: { contains: filters.search, mode: 'insensitive' } } },
    ];
  }

  const [tickets, total] = await Promise.all([
    prisma.support_tickets.findMany({
      where,
      include: {
        users: {
          select: {
            id: true,
            email: true,
            name: true,
            companyName: true,
          },
        },
      },
      orderBy: [{ priority: 'desc' }, { createdAt: 'desc' }],
      skip,
      take: limit,
    }),
    prisma.support_tickets.count({ where }),
  ]);

  return {
    tickets: tickets.map((ticket) => ({
      id: ticket.id,
      userId: ticket.userId,
      subject: ticket.subject,
      category: ticket.category as TicketCategory,
      priority: ticket.priority as TicketPriority,
      message: ticket.message,
      status: ticket.status as TicketStatus,
      attachments: ticket.attachments as string[] | null,
      resolvedAt: ticket.resolvedAt,
      resolvedBy: ticket.resolvedBy,
      resolution: ticket.resolution,
      createdAt: ticket.createdAt,
      updatedAt: ticket.updatedAt,
      user: {
        id: ticket.users.id,
        email: ticket.users.email,
        name: ticket.users.name,
        companyName: ticket.users.companyName,
      },
    })),
    total,
    page,
    totalPages: Math.ceil(total / limit),
  };
}

/**
 * Get a single ticket by ID
 */
export async function getTicketById(id: string): Promise<Ticket | null> {
  const ticket = await prisma.support_tickets.findUnique({
    where: { id },
    include: {
      users: {
        select: {
          id: true,
          email: true,
          name: true,
          companyName: true,
        },
      },
    },
  });

  if (!ticket) return null;

  return {
    id: ticket.id,
    userId: ticket.userId,
    subject: ticket.subject,
    category: ticket.category as TicketCategory,
    priority: ticket.priority as TicketPriority,
    message: ticket.message,
    status: ticket.status as TicketStatus,
    attachments: ticket.attachments as string[] | null,
    resolvedAt: ticket.resolvedAt,
    resolvedBy: ticket.resolvedBy,
    resolution: ticket.resolution,
    createdAt: ticket.createdAt,
    updatedAt: ticket.updatedAt,
    user: {
      id: ticket.users.id,
      email: ticket.users.email,
      name: ticket.users.name,
      companyName: ticket.users.companyName,
    },
  };
}

/**
 * Update ticket status
 */
export async function updateTicketStatus(
  id: string,
  status: TicketStatus,
  staffId?: string,
  resolution?: string
): Promise<Ticket | null> {
  const updateData: Record<string, unknown> = {
    status,
    updatedAt: new Date(),
  };

  if (status === 'RESOLVED' || status === 'CLOSED') {
    updateData.resolvedAt = new Date();
    if (staffId) updateData.resolvedBy = staffId;
    if (resolution) updateData.resolution = resolution;
  }

  const ticket = await prisma.support_tickets.update({
    where: { id },
    data: updateData,
    include: {
      users: {
        select: {
          id: true,
          email: true,
          name: true,
          companyName: true,
        },
      },
    },
  });

  return {
    id: ticket.id,
    userId: ticket.userId,
    subject: ticket.subject,
    category: ticket.category as TicketCategory,
    priority: ticket.priority as TicketPriority,
    message: ticket.message,
    status: ticket.status as TicketStatus,
    attachments: ticket.attachments as string[] | null,
    resolvedAt: ticket.resolvedAt,
    resolvedBy: ticket.resolvedBy,
    resolution: ticket.resolution,
    createdAt: ticket.createdAt,
    updatedAt: ticket.updatedAt,
    user: {
      id: ticket.users.id,
      email: ticket.users.email,
      name: ticket.users.name,
      companyName: ticket.users.companyName,
    },
  };
}

/**
 * Update ticket priority
 */
export async function updateTicketPriority(
  id: string,
  priority: TicketPriority
): Promise<Ticket | null> {
  const ticket = await prisma.support_tickets.update({
    where: { id },
    data: {
      priority,
      updatedAt: new Date(),
    },
    include: {
      users: {
        select: {
          id: true,
          email: true,
          name: true,
          companyName: true,
        },
      },
    },
  });

  return {
    id: ticket.id,
    userId: ticket.userId,
    subject: ticket.subject,
    category: ticket.category as TicketCategory,
    priority: ticket.priority as TicketPriority,
    message: ticket.message,
    status: ticket.status as TicketStatus,
    attachments: ticket.attachments as string[] | null,
    resolvedAt: ticket.resolvedAt,
    resolvedBy: ticket.resolvedBy,
    resolution: ticket.resolution,
    createdAt: ticket.createdAt,
    updatedAt: ticket.updatedAt,
    user: {
      id: ticket.users.id,
      email: ticket.users.email,
      name: ticket.users.name,
      companyName: ticket.users.companyName,
    },
  };
}

/**
 * Get ticket metrics
 */
export async function getTicketMetrics(): Promise<TicketMetrics> {
  const [
    totalOpen,
    totalInProgress,
    totalResolved,
    totalClosed,
    urgentTickets,
    resolvedTickets,
    categoryBreakdown,
    priorityBreakdown,
  ] = await Promise.all([
    prisma.support_tickets.count({ where: { status: 'OPEN' } }),
    prisma.support_tickets.count({ where: { status: 'IN_PROGRESS' } }),
    prisma.support_tickets.count({ where: { status: 'RESOLVED' } }),
    prisma.support_tickets.count({ where: { status: 'CLOSED' } }),
    prisma.support_tickets.count({
      where: { priority: 'URGENT', status: { in: ['OPEN', 'IN_PROGRESS'] } },
    }),
    prisma.support_tickets.findMany({
      where: {
        resolvedAt: { not: null },
      },
      select: {
        createdAt: true,
        resolvedAt: true,
      },
      take: 100,
      orderBy: { resolvedAt: 'desc' },
    }),
    prisma.support_tickets.groupBy({
      by: ['category'],
      _count: { id: true },
    }),
    prisma.support_tickets.groupBy({
      by: ['priority'],
      _count: { id: true },
    }),
  ]);

  // Calculate average resolution time
  let avgResolutionTimeHours = 0;
  if (resolvedTickets.length > 0) {
    const totalHours = resolvedTickets.reduce((sum, ticket) => {
      if (ticket.resolvedAt) {
        const diffMs = ticket.resolvedAt.getTime() - ticket.createdAt.getTime();
        return sum + diffMs / (1000 * 60 * 60);
      }
      return sum;
    }, 0);
    avgResolutionTimeHours = Math.round((totalHours / resolvedTickets.length) * 10) / 10;
  }

  const ticketsByCategory = {
    BILLING: 0,
    TECHNICAL: 0,
    FEATURE_REQUEST: 0,
    OTHER: 0,
  } as Record<TicketCategory, number>;

  categoryBreakdown.forEach((item) => {
    ticketsByCategory[item.category as TicketCategory] = item._count.id;
  });

  const ticketsByPriority = {
    LOW: 0,
    NORMAL: 0,
    URGENT: 0,
  } as Record<TicketPriority, number>;

  priorityBreakdown.forEach((item) => {
    ticketsByPriority[item.priority as TicketPriority] = item._count.id;
  });

  return {
    totalOpen,
    totalInProgress,
    totalResolved,
    totalClosed,
    avgResolutionTimeHours,
    ticketsByCategory,
    ticketsByPriority,
    urgentTickets,
  };
}

/**
 * Add resolution to a ticket
 */
export async function resolveTicket(
  id: string,
  staffId: string,
  resolution: string
): Promise<Ticket | null> {
  return updateTicketStatus(id, 'RESOLVED', staffId, resolution);
}

// ============================================================================
// User-Facing Functions - Ticket Messaging System
// ============================================================================

/**
 * Create a new support ticket (user-facing)
 * Creates the ticket and an initial message in the thread
 */
export async function createTicket(data: CreateTicketRequest): Promise<Ticket> {
  const ticketId = nanoid();

  const ticket = await prisma.$transaction(async (tx) => {
    // Create the ticket
    const newTicket = await tx.support_tickets.create({
      data: {
        id: ticketId,
        userId: data.userId,
        subject: data.subject,
        category: data.category,
        priority: data.priority || 'NORMAL',
        message: data.message,
        status: 'OPEN',
        attachments: data.attachments || null,
        updatedAt: new Date(),
      },
      include: {
        users: {
          select: { id: true, email: true, name: true, companyName: true },
        },
      },
    });

    // Create initial message in the thread
    await tx.ticket_messages.create({
      data: {
        ticketId: ticketId,
        senderId: data.userId,
        senderType: 'USER',
        message: data.message,
        attachments: data.attachments || null,
      },
    });

    return newTicket;
  });

  return {
    id: ticket.id,
    userId: ticket.userId,
    subject: ticket.subject,
    category: ticket.category as TicketCategory,
    priority: ticket.priority as TicketPriority,
    message: ticket.message,
    status: ticket.status as TicketStatus,
    attachments: ticket.attachments as string[] | null,
    resolvedAt: ticket.resolvedAt,
    resolvedBy: ticket.resolvedBy,
    resolution: ticket.resolution,
    createdAt: ticket.createdAt,
    updatedAt: ticket.updatedAt,
    user: {
      id: ticket.users.id,
      email: ticket.users.email,
      name: ticket.users.name,
      companyName: ticket.users.companyName,
    },
  };
}

/**
 * Get tickets for a specific user with pagination and unread counts
 */
export async function getUserTickets(
  userId: string,
  filters?: TicketFilters
): Promise<{
  tickets: (Ticket & { unreadCount: number })[];
  total: number;
  page: number;
  totalPages: number;
}> {
  const page = filters?.page || 1;
  const limit = filters?.limit || 20;
  const skip = (page - 1) * limit;

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const where: any = { userId };

  if (filters?.status) where.status = filters.status;
  if (filters?.priority) where.priority = filters.priority;
  if (filters?.category) where.category = filters.category;

  const [tickets, total] = await Promise.all([
    prisma.support_tickets.findMany({
      where,
      include: {
        users: {
          select: { id: true, email: true, name: true, companyName: true },
        },
        messages: {
          where: { senderType: 'STAFF', isRead: false },
          select: { id: true },
        },
      },
      orderBy: [{ updatedAt: 'desc' }],
      skip,
      take: limit,
    }),
    prisma.support_tickets.count({ where }),
  ]);

  return {
    tickets: tickets.map((ticket) => ({
      id: ticket.id,
      userId: ticket.userId,
      subject: ticket.subject,
      category: ticket.category as TicketCategory,
      priority: ticket.priority as TicketPriority,
      message: ticket.message,
      status: ticket.status as TicketStatus,
      attachments: ticket.attachments as string[] | null,
      resolvedAt: ticket.resolvedAt,
      resolvedBy: ticket.resolvedBy,
      resolution: ticket.resolution,
      createdAt: ticket.createdAt,
      updatedAt: ticket.updatedAt,
      user: {
        id: ticket.users.id,
        email: ticket.users.email,
        name: ticket.users.name,
        companyName: ticket.users.companyName,
      },
      unreadCount: ticket.messages.length,
    })),
    total,
    page,
    totalPages: Math.ceil(total / limit),
  };
}

/**
 * Get a ticket with its full message thread
 * If userId is provided, verifies ownership
 */
export async function getTicketWithMessages(
  ticketId: string,
  userId?: string
): Promise<TicketWithMessages | null> {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const where: any = { id: ticketId };
  if (userId) where.userId = userId;

  const ticket = await prisma.support_tickets.findFirst({
    where,
    include: {
      users: {
        select: { id: true, email: true, name: true, companyName: true },
      },
      messages: {
        orderBy: { createdAt: 'asc' },
      },
    },
  });

  if (!ticket) return null;

  // Get sender names for messages
  // NOTE: Both regular users AND staff users are stored in the `users` table
  // Staff users have roles: ADMIN, SUPER_ADMIN, STAFF
  // There is NO separate staff_users table
  const senderIds = [...new Set(ticket.messages.map((m) => m.senderId))];
  const senders = await prisma.users.findMany({
    where: { id: { in: senderIds } },
    select: { id: true, name: true, email: true },
  });
  const senderMap = new Map(senders.map((s) => [s.id, s]));

  const unreadCount = ticket.messages.filter(
    (m) => m.senderType === 'STAFF' && !m.isRead
  ).length;

  return {
    id: ticket.id,
    userId: ticket.userId,
    subject: ticket.subject,
    category: ticket.category as TicketCategory,
    priority: ticket.priority as TicketPriority,
    message: ticket.message,
    status: ticket.status as TicketStatus,
    attachments: ticket.attachments as string[] | null,
    resolvedAt: ticket.resolvedAt,
    resolvedBy: ticket.resolvedBy,
    resolution: ticket.resolution,
    createdAt: ticket.createdAt,
    updatedAt: ticket.updatedAt,
    user: {
      id: ticket.users.id,
      email: ticket.users.email,
      name: ticket.users.name,
      companyName: ticket.users.companyName,
    },
    messageThread: ticket.messages.map((m) => {
      const sender = senderMap.get(m.senderId);
      return {
        id: m.id,
        ticketId: m.ticketId,
        senderId: m.senderId,
        senderType: m.senderType as TicketSenderType,
        message: m.message,
        attachments: m.attachments as string[] | null,
        isRead: m.isRead,
        readAt: m.readAt,
        createdAt: m.createdAt,
        senderName: sender?.name || undefined,
        senderEmail: sender?.email || undefined,
      };
    }),
    unreadCount,
  };
}

/**
 * Add a message to a ticket thread
 */
export async function addTicketMessage(
  ticketId: string,
  senderId: string,
  senderType: TicketSenderType,
  message: string,
  attachments?: string[]
): Promise<TicketMessage> {
  const [newMessage] = await prisma.$transaction([
    prisma.ticket_messages.create({
      data: {
        ticketId,
        senderId,
        senderType,
        message,
        attachments: attachments || null,
      },
    }),
    // Update ticket's updatedAt timestamp
    prisma.support_tickets.update({
      where: { id: ticketId },
      data: { updatedAt: new Date() },
    }),
  ]);

  // Get sender info
  // NOTE: Both regular users AND staff users are stored in the `users` table
  // Staff users have roles: ADMIN, SUPER_ADMIN, STAFF
  // There is NO separate staff_users table
  let senderName: string | undefined;
  let senderEmail: string | undefined;

  const sender = await prisma.users.findUnique({
    where: { id: senderId },
    select: { name: true, email: true },
  });
  senderName = sender?.name || undefined;
  senderEmail = sender?.email || undefined;

  return {
    id: newMessage.id,
    ticketId: newMessage.ticketId,
    senderId: newMessage.senderId,
    senderType: newMessage.senderType as TicketSenderType,
    message: newMessage.message,
    attachments: newMessage.attachments as string[] | null,
    isRead: newMessage.isRead,
    readAt: newMessage.readAt,
    createdAt: newMessage.createdAt,
    senderName,
    senderEmail,
  };
}

/**
 * Mark messages as read for a specific ticket
 * @param ticketId - The ticket ID
 * @param markType - Which messages to mark: 'staff_messages' (for user viewing) or 'user_messages' (for staff viewing)
 */
export async function markMessagesAsRead(
  ticketId: string,
  markType: 'staff_messages' | 'user_messages'
): Promise<number> {
  const senderTypeToMark = markType === 'staff_messages' ? 'STAFF' : 'USER';

  const result = await prisma.ticket_messages.updateMany({
    where: {
      ticketId,
      senderType: senderTypeToMark,
      isRead: false,
    },
    data: {
      isRead: true,
      readAt: new Date(),
    },
  });

  return result.count;
}

/**
 * Get total unread message count for a user (staff replies they haven't read)
 */
export async function getUnreadMessageCount(userId: string): Promise<number> {
  const count = await prisma.ticket_messages.count({
    where: {
      ticket: { userId },
      senderType: 'STAFF',
      isRead: false,
    },
  });

  return count;
}

/**
 * Get unread message count for staff (user messages they haven't read)
 */
export async function getStaffUnreadCount(): Promise<number> {
  const count = await prisma.ticket_messages.count({
    where: {
      senderType: 'USER',
      isRead: false,
    },
  });

  return count;
}
