import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";

import {
  ConversationStatus,
  DeliveryStatus,
  MessageDirection,
  RoleCode,
  type Prisma
} from "@prisma/client";

import { getSession } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { assertPermission, type ActionPermission } from "@/lib/permissions";
import { getRequestAuditContext, writeAuditTrail } from "@/lib/audit";
import { sendOutboundTextMessage } from "@/lib/whatsapp-provider";

type AppRole = "admin" | "agent";
type InboxScope = "all" | "unassigned" | "resolved" | "open" | "pending";

export type ConversationSummary = {
  id: string;
  tenantId: string;
  name: string;
  phone: string;
  snippet: string;
  time: string;
  status: "Open" | "Pending" | "Resolved";
  assignee: string;
  assigneeId: string | null;
  channel: string;
  tags: string[];
  priority: "Normal" | "High";
  contactId: string;
};

export type ConversationMessage = {
  id: string;
  body: string;
  direction: "INBOUND" | "OUTBOUND";
  from: string;
  at: string;
};

export type ConversationNote = {
  id: string;
  content: string;
  by: string;
  at: string;
};

export type MentionedConversationRecord = {
  id: string;
  contactName: string;
  mentionedBy: string;
  time: string;
  snippet: string;
};

export type ResolvedConversationRecord = {
  id: string;
  contactName: string;
  resolvedAt: string;
  lastAction: string;
};

export type InboxConversationDetail = ConversationSummary & {
  tagJson: string;
  messages: ConversationMessage[];
  notes: ConversationNote[];
};

type ListOptions = {
  scope: AppRole;
  conversationId?: string | null;
  filter?: InboxScope;
};

function formValue(formData: FormData, name: string, fallback = "") {
  const value = formData.get(name);
  return typeof value === "string" ? value.trim() : fallback;
}

function parseRole(sessionRole: string): AppRole | null {
  if (sessionRole === "admin_client") {
    return "admin";
  }

  if (sessionRole === "agent") {
    return "agent";
  }

  return null;
}

function mapConversationStatus(status: ConversationStatus): "Open" | "Pending" | "Resolved" {
  if (status === ConversationStatus.PENDING) {
    return "Pending";
  }

  if (status === ConversationStatus.RESOLVED) {
    return "Resolved";
  }

  return "Open";
}

function mapConversationPriority(value: "LOW" | "NORMAL" | "HIGH" | "URGENT" | string) {
  return value === "HIGH" || value === "URGENT" ? "High" : "Normal";
}

function formatRelativeDate(date: Date | null | undefined) {
  if (!date) {
    return "-";
  }

  const now = new Date();
  const diff = now.getTime() - date.getTime();
  const day = 1000 * 60 * 60 * 24;

  if (diff < day) {
    return new Intl.DateTimeFormat("id-ID", {
      hour: "2-digit",
      minute: "2-digit"
    }).format(date);
  }

  if (diff < day * 2) {
    return "Yesterday";
  }

  return new Intl.DateTimeFormat("id-ID", {
    day: "2-digit",
    month: "short",
    year: "numeric"
  }).format(date);
}

function formatDateTime(date: Date | null | undefined) {
  if (!date) {
    return "-";
  }

  return new Intl.DateTimeFormat("id-ID", {
    day: "2-digit",
    month: "short",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit"
  }).format(date);
}

function mapStatusForSelect(status: ConversationStatus): string {
  return status;
}

async function requireInboxScope(scope: AppRole, permission?: ActionPermission) {
  const session = await getSession();
  const role = session?.role ? parseRole(session.role) : null;

  if (!session || !role) {
    redirect("/login");
  }

  if (role !== scope && !(scope === "admin" && session.role === "super_admin")) {
    redirect("/unauthorized");
  }

  if (permission && !assertPermission(session.role, permission, session.extraPermissions)) {
    const requestContext = await getRequestAuditContext();
    await writeAuditTrail({
      tenantId: session.tenantId,
      actorUserId: session.userId,
      entityType: "user",
      entityId: session.userId,
      action: "permission_denied",
      metadata: {
        action: `inbox_scope_${scope}`,
        requiredPermission: permission,
        requestScope: scope
      },
      ipAddress: requestContext.ipAddress,
      userAgent: requestContext.userAgent
    });

    redirect("/unauthorized");
  }

  return { session, role };
}

async function requireInboxActor(permission?: ActionPermission) {
  const session = await getSession();
  if (!session) {
    redirect("/login");
  }

  const role = parseRole(session.role);
  if (!role) {
    redirect("/unauthorized");
  }

  if (permission && !assertPermission(session.role, permission, session.extraPermissions)) {
    const requestContext = await getRequestAuditContext();
    await writeAuditTrail({
      tenantId: session.tenantId,
      actorUserId: session.userId,
      entityType: "user",
      entityId: session.userId,
      action: "permission_denied",
      metadata: {
        action: "inbox_actor",
        requiredPermission: permission,
        requestScope: scopeLabel(role)
      },
      ipAddress: requestContext.ipAddress,
      userAgent: requestContext.userAgent
    });

    redirect("/unauthorized");
  }

  return { session, role };
}

function scopeLabel(role: AppRole) {
  return role === "admin" ? "admin_scope" : "agent_scope";
}

type AuditActorSession = {
  tenantId: string;
  userId: string;
};

async function getAuditContext(session: AuditActorSession) {
  const requestContext = await getRequestAuditContext();
  return {
    tenantId: session.tenantId,
    actorUserId: session.userId,
    ipAddress: requestContext.ipAddress,
    userAgent: requestContext.userAgent
  };
}

function buildWhere(role: AppRole, tenantId: string, userId: string, filter: InboxScope) {
  const base = { tenantId };
  if (role === "admin") {
    if (filter === "unassigned") {
      return { ...base, assignedUserId: null };
    }

    if (filter === "resolved") {
      return { ...base, status: ConversationStatus.RESOLVED };
    }

    if (filter === "pending") {
      return { ...base, status: ConversationStatus.PENDING };
    }

    if (filter === "open") {
      return { ...base, status: ConversationStatus.OPEN };
    }

    return base;
  }

  if (filter === "resolved") {
    return { ...base, status: ConversationStatus.RESOLVED, assignedUserId: userId };
  }

  if (filter === "unassigned") {
    return { ...base, assignedUserId: null };
  }

  if (filter === "pending") {
    return { ...base, status: ConversationStatus.PENDING, assignedUserId: userId };
  }

  if (filter === "open") {
    return { ...base, status: ConversationStatus.OPEN, assignedUserId: userId };
  }

  return { ...base, assignedUserId: userId };
}

function mentionNeedles(fullName: string) {
  const normalized = fullName
    .toLowerCase()
    .split(" ")
    .filter(Boolean);

  const unique = [fullName.toLowerCase(), ...normalized];

  return [...new Set(unique)].map((name) => `@${name}`);
}

function textContainsAny(value: string | null, needles: string[]) {
  if (!value) {
    return false;
  }

  const target = value.toLowerCase();
  return needles.some((needle) => target.includes(needle.toLowerCase()));
}

export async function getInboxWorkspace({ scope, conversationId, filter = "all" }: ListOptions) {
  const { session, role } = await requireInboxScope(scope, "inbox:read");
  const agentFilter = role === "agent";
  const where = buildWhere(role, session.tenantId, session.userId, filter);

  const conversations = await prisma.conversation.findMany({
    where,
    orderBy: { lastMessageAt: "desc" },
    include: {
      contact: true,
      channel: true,
      assignedUser: true,
      conversationTags: {
        include: { tag: true }
      },
      messages: {
        take: 1,
        orderBy: { createdAt: "desc" }
      }
    }
  });

  const mapped = conversations.map((conversation) => {
    const latestMessage = conversation.messages[0];
    return {
      id: conversation.id,
      tenantId: conversation.tenantId,
      name: conversation.contact.fullName,
      phone: conversation.contact.phoneNumber,
      snippet: latestMessage?.contentText ?? conversation.subject ?? "No content",
      time: formatRelativeDate(conversation.lastMessageAt),
      status: mapConversationStatus(conversation.status),
      assignee: conversation.assignedUser?.fullName ?? "Unassigned",
      assigneeId: conversation.assignedUser?.id ?? null,
      channel: conversation.channel.channelName,
      tags: conversation.conversationTags.map((item) => item.tag.name),
      priority: mapConversationPriority(conversation.priority),
      contactId: conversation.contactId
    } satisfies ConversationSummary;
  });

  const selected = conversationId
    ? mapped.find((item) => item.id === conversationId) || mapped[0]
    : mapped[0];

  if (!selected) {
    return {
      conversations: mapped,
      selectedConversation: null,
      canSelfAssign: role === "agent",
      role,
      agents: [] as Array<{ id: string; name: string }>,
      filter,
      defaultPath: scope === "admin" ? "/inbox" : "/agent/inbox"
    };
  }

  const fullConversation = await prisma.conversation.findUnique({
    where: {
      id: selected.id
    },
    include: {
      contact: true,
      channel: true,
      assignedUser: true,
      messages: {
        include: { sentByUser: true, contact: true },
        orderBy: { createdAt: "desc" },
        take: 30
      },
      conversationTags: { include: { tag: true } },
      notes: {
        include: { user: true },
        orderBy: { createdAt: "desc" }
      }
    }
  });

  const tenantScopedFilter = agentFilter
    ? { tenantId: session.tenantId, role: { code: RoleCode.AGENT } }
    : { tenantId: session.tenantId, role: { code: { in: [RoleCode.ADMIN_CLIENT, RoleCode.AGENT] } } };

  const agents = await prisma.user.findMany({
    where: tenantScopedFilter,
    orderBy: [{ fullName: "asc" }]
  });

  const detail: InboxConversationDetail | null = fullConversation
    ? {
        id: fullConversation.id,
        tenantId: fullConversation.tenantId,
        name: fullConversation.contact.fullName,
        phone: fullConversation.contact.phoneNumber,
        snippet: fullConversation.subject ?? fullConversation.messages[0]?.contentText ?? "No content",
        time: formatRelativeDate(fullConversation.lastMessageAt),
        status: mapConversationStatus(fullConversation.status),
        assignee: fullConversation.assignedUser?.fullName ?? "Unassigned",
        assigneeId: fullConversation.assignedUser?.id ?? null,
        channel: fullConversation.channel.channelName,
        tags: fullConversation.conversationTags.map((item) => item.tag.name),
        priority: mapConversationPriority(fullConversation.priority),
        contactId: fullConversation.contact.id,
        tagJson: JSON.stringify(fullConversation.conversationTags.map((item) => item.tag.name)),
        messages: fullConversation.messages
          .slice()
          .reverse()
          .map((message) => {
            const from =
              message.direction === MessageDirection.OUTBOUND
                ? message.sentByUser?.fullName ?? "Agent"
                : fullConversation.contact.fullName;
            return {
              id: message.id,
              body: message.contentText ?? "",
              direction: message.direction === MessageDirection.INBOUND ? "INBOUND" : "OUTBOUND",
              from,
              at: formatDateTime(message.createdAt)
            };
          }),
        notes: fullConversation.notes.map((note) => ({
          id: note.id,
          content: note.content,
          by: note.user?.fullName ?? "System",
          at: formatDateTime(note.createdAt)
        }))
      }
    : null;

  if (!detail) {
    return {
      conversations: mapped,
      selectedConversation: null,
      canSelfAssign: role === "agent",
      role,
      agents: [],
      filter,
      defaultPath: scope === "admin" ? "/inbox" : "/agent/inbox"
    };
  }

  return {
    conversations: mapped,
    selectedConversation: detail,
    canSelfAssign: role === "agent",
    role,
    agents: agents.map((agent) => ({ id: agent.id, name: agent.fullName })),
    filter,
    defaultPath: scope === "admin" ? "/inbox" : "/agent/inbox"
  };
}

export async function getAgentMentionedConversations() {
  const { session, role } = await requireInboxActor("inbox:read");
  if (role !== "agent") {
    redirect("/unauthorized");
  }

  const needles = mentionNeedles(session.fullName);
  const tenantConversations = await prisma.conversation.findMany({
    where: {
      tenantId: session.tenantId,
      OR: [
        {
          messages: {
            some: {
              contentText: {
                contains: needles[0]
              }
            }
          }
        },
        {
          messages: {
            some: {
              contentText: {
                contains: needles[1] ?? ""
              }
            }
          }
        },
        {
          notes: {
            some: {
              content: {
                contains: needles[0]
              }
            }
          }
        },
        {
          notes: {
            some: {
              content: {
                contains: needles[1] ?? ""
              }
            }
          }
        }
      ]
    },
    include: {
      contact: true,
      messages: {
        include: {
          sentByUser: true,
          contact: true
        },
        orderBy: { createdAt: "desc" },
        take: 15
      },
      notes: {
        include: { user: true },
        orderBy: { createdAt: "desc" },
        take: 15
      }
    },
    orderBy: { lastMessageAt: "desc" }
  });

  const mapped = tenantConversations.map((conversation) => {
    const noteHit = conversation.notes.find((note) => textContainsAny(note.content, needles));
    const messageHit = conversation.messages.find((message) => textContainsAny(message.contentText ?? "", needles));

    let mentionedBy = "System";
    let mentionTime: Date | null = conversation.lastMessageAt;

    if (noteHit) {
      mentionedBy = noteHit.user?.fullName ?? "System";
      mentionTime = noteHit.createdAt;
    } else if (messageHit) {
      mentionedBy =
        messageHit.direction === MessageDirection.INBOUND
          ? conversation.contact.fullName
          : messageHit.sentByUser?.fullName ?? "Agent";
      mentionTime = messageHit.createdAt;
    }

    return {
      id: conversation.id,
      contactName: conversation.contact.fullName,
      mentionedBy,
      time: formatDateTime(mentionTime),
      snippet: conversation.messages[0]?.contentText ?? conversation.subject ?? "No content"
    } satisfies MentionedConversationRecord;
  });

  return mapped;
}

export async function getAgentResolvedHistory() {
  const { session, role } = await requireInboxActor("inbox:read");
  if (role !== "agent") {
    redirect("/unauthorized");
  }

  const conversations = await prisma.conversation.findMany({
    where: {
      tenantId: session.tenantId,
      assignedUserId: session.userId,
      status: ConversationStatus.RESOLVED
    },
    include: {
      contact: true,
      activities: {
        orderBy: { createdAt: "desc" },
        take: 1
      }
    },
    orderBy: { resolvedAt: "desc" }
  });

  const mapLastAction = (activities: Array<{ activityType: string }>) => {
    const lastActivity = activities[0]?.activityType;
    if (lastActivity === "MESSAGE_SENT") {
      return "Reply sent";
    }

    if (lastActivity === "STATUS_UPDATED") {
      return "Status updated";
    }

    if (lastActivity === "ASSIGNED") {
      return "Assigned";
    }

    return lastActivity ? `Last action: ${lastActivity}` : "No action";
  };

  return conversations.map((conversation) => ({
    id: conversation.id,
    contactName: conversation.contact.fullName,
    resolvedAt: formatDateTime(conversation.resolvedAt),
    lastAction: mapLastAction(conversation.activities)
  }) satisfies ResolvedConversationRecord);
}

export async function assignConversation(formData: FormData) {
  const { session, role } = await requireInboxActor("inbox:assign");
  const conversationId = formValue(formData, "conversationId");
  const assigneeId = formValue(formData, "assigneeId");
  const nextPath = formValue(formData, "nextPath", role === "admin" ? "/inbox" : "/agent/inbox");
  const auditContext = await getAuditContext(session);

  if (!conversationId) {
    redirect(nextPath);
  }

  const conversation = await prisma.conversation.findFirst({
    where: { id: conversationId, tenantId: session.tenantId }
  });
  if (!conversation) {
    redirect(`${nextPath}?error=conversation_not_found`);
  }

  let normalizedAssignee: string | null = assigneeId || null;
  if (role === "agent") {
    normalizedAssignee = session.userId;
  } else if (assigneeId) {
    const assignee = await prisma.user.findFirst({
      where: {
        id: assigneeId,
        tenantId: session.tenantId,
        role: { code: RoleCode.AGENT }
      }
    });

    if (!assignee) {
      redirect(`${nextPath}?error=invalid_agent`);
    }
  }

  await prisma.$transaction([
    prisma.conversation.update({
      where: { id: conversationId },
      data: { assignedUserId: normalizedAssignee }
    }),
    prisma.conversationActivity.create({
      data: {
        tenantId: session.tenantId,
        conversationId,
        actorUserId: session.userId,
        activityType: "ASSIGNED",
        metadataJson: JSON.stringify({
          assigneeId: normalizedAssignee ?? null
        })
      }
    })
  ]);

  await writeAuditTrail({
    ...auditContext,
    entityType: "conversation",
    entityId: conversationId,
    action: role === "agent" ? "self_assign_conversation" : "assign_conversation",
    metadata: {
      assigneeId: normalizedAssignee,
      previousAssigneeId: conversation.assignedUserId
    }
  });

  revalidatePath("/inbox");
  revalidatePath("/agent/inbox");
  redirect(`${nextPath}?conversationId=${conversationId}`);
}

export async function updateConversationStatus(formData: FormData) {
  const { session, role } = await requireInboxActor("inbox:status");
  const conversationId = formValue(formData, "conversationId");
  const statusRaw = formValue(formData, "status");
  const nextPath = formValue(formData, "nextPath", role === "admin" ? "/inbox" : "/agent/inbox");
  const auditContext = await getAuditContext(session);

  const validStatuses = [
    ConversationStatus.OPEN,
    ConversationStatus.PENDING,
    ConversationStatus.RESOLVED,
    ConversationStatus.ARCHIVED,
    ConversationStatus.SPAM
  ];
  if (!conversationId || !validStatuses.includes(statusRaw as ConversationStatus)) {
    redirect(`${nextPath}?error=invalid_status`);
  }

  const status = statusRaw as ConversationStatus;
  const conversation = await prisma.conversation.findFirst({
    where: { id: conversationId, tenantId: session.tenantId }
  });
  if (!conversation) {
    redirect(`${nextPath}?error=conversation_not_found`);
  }

  await prisma.$transaction([
    prisma.conversation.update({
      where: { id: conversationId },
      data: {
        status,
        resolvedAt: status === ConversationStatus.RESOLVED ? new Date() : status === ConversationStatus.OPEN ? null : conversation.resolvedAt
      }
    }),
    prisma.conversationActivity.create({
      data: {
        tenantId: session.tenantId,
        conversationId,
        actorUserId: session.userId,
        activityType: "STATUS_UPDATED",
        metadataJson: JSON.stringify({
          from: mapStatusForSelect(conversation.status),
          to: mapStatusForSelect(status)
        })
      }
    })
  ]);

  await writeAuditTrail({
    ...auditContext,
    entityType: "conversation",
    entityId: conversationId,
    action: "update_conversation_status",
    metadata: {
      from: mapStatusForSelect(conversation.status),
      to: mapStatusForSelect(status)
    }
  });

  revalidatePath("/inbox");
  revalidatePath("/agent/inbox");
  redirect(`${nextPath}?conversationId=${conversationId}`);
}

export async function saveConversationNote(formData: FormData) {
  const { session, role } = await requireInboxActor("inbox:notes");
  const conversationId = formValue(formData, "conversationId");
  const note = formValue(formData, "note");
  const nextPath = formValue(formData, "nextPath", role === "admin" ? "/inbox" : "/agent/inbox");
  const auditContext = await getAuditContext(session);

  if (!conversationId || !note) {
    redirect(`${nextPath}${conversationId ? `?conversationId=${conversationId}` : ""}`);
  }

  const conversation = await prisma.conversation.findFirst({
    where: { id: conversationId, tenantId: session.tenantId }
  });
  if (!conversation) {
    redirect(`${nextPath}?error=conversation_not_found`);
  }

  await prisma.conversationNote.create({
    data: {
      tenantId: session.tenantId,
      conversationId,
      userId: session.userId,
      content: note
    }
  });

  await writeAuditTrail({
    ...auditContext,
    entityType: "conversation",
    entityId: conversationId,
    action: "add_conversation_note",
    metadata: {
      note
    }
  });

  revalidatePath("/inbox");
  revalidatePath("/agent/inbox");
  redirect(`${nextPath}?conversationId=${conversationId}`);
}

export async function sendConversationReply(formData: FormData) {
  const { session, role } = await requireInboxActor("inbox:reply");
  const conversationId = formValue(formData, "conversationId");
  const content = formValue(formData, "content");
  const nextPath = formValue(formData, "nextPath", role === "admin" ? "/inbox" : "/agent/inbox");
  const auditContext = await getAuditContext(session);

  if (!conversationId || !content) {
    redirect(`${nextPath}${conversationId ? `?conversationId=${conversationId}` : ""}`);
  }

  const conversation = await prisma.conversation.findUnique({
    where: { id: conversationId },
    include: { contact: true, channel: true }
  });
  if (!conversation || conversation.tenantId !== session.tenantId) {
    redirect(`${nextPath}?error=conversation_not_found`);
  }

  if (!conversation.contactId || conversation.assignedUserId !== session.userId && role === "agent") {
    if (role === "agent") {
      redirect(`${nextPath}?conversationId=${conversationId}&error=not_assigned`);
    }
  }

  const now = new Date();

  const createdMessage = await prisma.message.create({
    data: {
      tenantId: session.tenantId,
      conversationId,
      channelId: conversation.channelId,
      contactId: conversation.contactId,
      direction: MessageDirection.OUTBOUND,
      type: "TEXT" as Prisma.MessageCreateManyInput["type"],
      contentText: content,
      deliveryStatus: DeliveryStatus.QUEUED,
      sentByUserId: session.userId,
      sentAt: now
    }
  });

  const outboundResult = await sendOutboundTextMessage({
    tenantId: session.tenantId,
    channelId: conversation.channelId,
    channelProvider: conversation.channel.provider,
    phoneNumberId: conversation.channel.phoneNumberId,
    to: conversation.contact.phoneNumber,
    content,
    messageId: createdMessage.id
  });

  const metadata = {
    snippet: content.slice(0, 120),
    provider: outboundResult.provider,
    status: outboundResult.deliveryStatus
  } as const;

  const shouldMarkChannelHealthy = outboundResult.success;

  await prisma.$transaction([
    prisma.message.update({
      where: { id: createdMessage.id },
      data: {
        providerMessageId: outboundResult.providerMessageId,
        deliveryStatus: outboundResult.deliveryStatus,
        failedReason: outboundResult.failureReason
      }
    }),
    prisma.conversation.update({
      where: { id: conversationId },
      data: {
        lastMessageAt: now,
        lastOutboundAt: now,
        status: ConversationStatus.OPEN
      }
    }),
    prisma.conversationActivity.create({
      data: {
        tenantId: session.tenantId,
        conversationId,
        actorUserId: session.userId,
        activityType: "MESSAGE_SENT",
        metadataJson: JSON.stringify(metadata)
      }
    }),
    prisma.webhookEvent.create({
      data: {
        tenantId: session.tenantId,
        channelId: conversation.channelId,
        eventType: "message.send_request",
        providerEventId: createdMessage.id,
        payloadJson: JSON.stringify({
          messageId: createdMessage.id,
          conversationId,
          contactPhone: conversation.contact.phoneNumber,
          provider: outboundResult.provider,
          failureReason: outboundResult.failureReason ?? null
        }),
        processStatus: outboundResult.success ? "processed" : "failed",
        failedReason: outboundResult.failureReason ?? null,
      }
    }),
    prisma.channel.update({
      where: { id: conversation.channelId },
      data: {
        webhookStatus: shouldMarkChannelHealthy ? "healthy" : "error",
        lastSyncAt: now
      }
    })
  ]);

  await writeAuditTrail({
    ...auditContext,
    entityType: "conversation",
    entityId: conversationId,
    action: "reply_conversation",
    metadata: {
      messageId: createdMessage.id,
      replyLength: content.length,
      channelId: conversation.channelId,
      deliveryStatus: outboundResult.deliveryStatus,
      failureReason: outboundResult.failureReason ?? null
    }
  });

  if (!outboundResult.success) {
    redirect(`${nextPath}?conversationId=${conversationId}&error=reply_failed`);
  }

  revalidatePath("/inbox");
  revalidatePath("/agent/inbox");
  redirect(`${nextPath}?conversationId=${conversationId}`);
}

export async function updateConversationTags(formData: FormData) {
  const { session, role } = await requireInboxActor("inbox:tags");
  const conversationId = formValue(formData, "conversationId");
  const tagsRaw = formValue(formData, "tags");
  const nextPath = formValue(formData, "nextPath", role === "admin" ? "/inbox" : "/agent/inbox");
  const auditContext = await getAuditContext(session);

  if (!conversationId) {
    redirect(`${nextPath}?error=invalid_conversation`);
  }

  const conversation = await prisma.conversation.findFirst({
    where: { id: conversationId, tenantId: session.tenantId }
  });
  if (!conversation) {
    redirect(`${nextPath}?error=conversation_not_found`);
  }

  const tags = tagsRaw
    .split(",")
    .map((tag) => tag.trim())
    .filter(Boolean);
  const dedupedTags = [...new Set(tags)];

  await prisma.$transaction(async (tx) => {
    await tx.conversationTag.deleteMany({
      where: { conversationId }
    });

    for (const tagName of dedupedTags) {
      const tag = await tx.tag.upsert({
        where: {
          tenantId_name: {
            tenantId: session.tenantId,
            name: tagName
          }
        },
        create: {
          tenantId: session.tenantId,
          name: tagName
        },
        update: {}
      });

      await tx.conversationTag.create({
        data: {
          tenantId: session.tenantId,
          conversationId,
          tagId: tag.id
        }
      });
    }

    await tx.conversationActivity.create({
      data: {
        tenantId: session.tenantId,
        conversationId,
        actorUserId: session.userId,
        activityType: "TAGS_UPDATED",
        metadataJson: JSON.stringify({ tags })
      }
    });
  });

  await writeAuditTrail({
    ...auditContext,
    entityType: "conversation",
    entityId: conversationId,
    action: "update_conversation_tags",
    metadata: {
      tags: dedupedTags
    }
  });

  revalidatePath("/inbox");
  revalidatePath("/agent/inbox");
  redirect(`${nextPath}?conversationId=${conversationId}`);
}

export async function addConversationNote(formData: FormData) {
  return saveConversationNote(formData);
}

export async function replyToConversation(formData: FormData) {
  return sendConversationReply(formData);
}

export async function setConversationTags(formData: FormData) {
  return updateConversationTags(formData);
}
