import { Router } from "express";
import {
  Prisma,
  SupportConversationStatus,
  UserRole,
} from "@chat-service/prisma-client";
import { prisma } from "../../db/prisma.js";
import { requireAdmin, requireAuth, type AuthenticatedRequest } from "../../middleware/auth.js";
import { emitSupportConversationUpdated, emitSupportMessageCreated } from "../../realtime.js";
import { env } from "../../config/env.js";

const conversationListInclude = {
  messages: {
    take: 1,
    orderBy: {
      createdAt: "desc"
    }
  }
} satisfies Prisma.SupportConversationInclude;

const conversationDetailsInclude = {
  messages: {
    orderBy: {
      createdAt: "asc"
    }
  }
} satisfies Prisma.SupportConversationInclude;

type ConversationListRecord = Prisma.SupportConversationGetPayload<{ include: typeof conversationListInclude }>;
type ConversationDetailsRecord = Prisma.SupportConversationGetPayload<{ include: typeof conversationDetailsInclude }>;
type MessageRecord = ConversationDetailsRecord["messages"][number];

function hasUnreadForAdmin(conversation: { lastUserMessageAt: Date | null; adminLastReadAt: Date | null }) {
  if (!conversation.lastUserMessageAt) return false;
  if (!conversation.adminLastReadAt) return true;
  return conversation.lastUserMessageAt > conversation.adminLastReadAt;
}

function hasUnreadForUser(conversation: { lastAdminMessageAt: Date | null; userLastReadAt: Date | null }) {
  if (!conversation.lastAdminMessageAt) return false;
  if (!conversation.userLastReadAt) return true;
  return conversation.lastAdminMessageAt > conversation.userLastReadAt;
}

function mapMessage(message: MessageRecord) {
  return {
    id: message.id,
    conversationId: message.conversationId,
    body: message.body,
    createdAt: message.createdAt,
    updatedAt: message.updatedAt,
    author: {
      id: message.authorId,
      email: message.authorEmail,
      role: message.authorRole
    }
  };
}

function mapConversation(conversation: ConversationListRecord | ConversationDetailsRecord) {
  const latestMessage = conversation.messages.at(-1) ?? null;

  return {
    id: conversation.id,
    status: conversation.status,
    createdAt: conversation.createdAt,
    updatedAt: conversation.updatedAt,
    lastMessageAt: conversation.lastMessageAt,
    lastUserMessageAt: conversation.lastUserMessageAt,
    lastAdminMessageAt: conversation.lastAdminMessageAt,
    unreadForAdmin: hasUnreadForAdmin(conversation),
    unreadForUser: hasUnreadForUser(conversation),
    user: {
      id: conversation.userId,
      email: conversation.userEmail,
      role: "user" as const,
      active: true
    },
    assignedAdmin: conversation.assignedAdminId
      ? {
          id: conversation.assignedAdminId,
          email: conversation.assignedAdminEmail ?? "admin",
          role: "admin" as const
        }
      : null,
    latestMessage: latestMessage ? mapMessage(latestMessage as MessageRecord) : null,
    messages:
      conversation.messages.length > 1 || (latestMessage && "body" in latestMessage)
        ? conversation.messages.map((message) => mapMessage(message as MessageRecord))
        : undefined
  };
}

async function ensureUserConversation(userId: string, userEmail: string) {
  return prisma.supportConversation.upsert({
    where: { userId },
    update: {
      userEmail
    },
    create: {
      userId,
      userEmail,
      userLastReadAt: new Date()
    },
    include: conversationDetailsInclude
  });
}

async function createSupportMessage(options: {
  conversationId: string;
  authorId: string;
  authorEmail: string;
  authorRole: UserRole;
  body: string;
}) {
  const now = new Date();

  await prisma.supportMessage.create({
    data: {
      conversationId: options.conversationId,
      authorId: options.authorId,
      authorEmail: options.authorEmail,
      authorRole: options.authorRole,
      body: options.body
    }
  });

  return prisma.supportConversation.update({
    where: { id: options.conversationId },
    data: {
      status: SupportConversationStatus.open,
      assignedAdminId: options.authorRole === UserRole.admin ? options.authorId : undefined,
      assignedAdminEmail: options.authorRole === UserRole.admin ? options.authorEmail : undefined,
      lastMessageAt: now,
      lastUserMessageAt: options.authorRole === UserRole.user ? now : undefined,
      lastAdminMessageAt: options.authorRole === UserRole.admin ? now : undefined,
      userLastReadAt: options.authorRole === UserRole.user ? now : undefined,
      adminLastReadAt: options.authorRole === UserRole.admin ? now : undefined
    },
    include: conversationDetailsInclude
  });
}

function extractEmail(req: AuthenticatedRequest) {
  const emailHeader = req.headers["x-user-email"];
  if (typeof emailHeader === "string" && emailHeader.trim()) {
    return emailHeader.trim();
  }

  if (Array.isArray(emailHeader)) {
    const first = emailHeader.find((value) => typeof value === "string" && value.trim());
    if (first) return first.trim();
  }

  return `${req.user!.id}@chat.local`;
}

function extractBody(req: AuthenticatedRequest) {
  const body = typeof req.body?.body === "string" ? req.body.body.trim() : "";
  if (!body) return null;
  return body.slice(0, env.CHAT_MESSAGE_MAX_LENGTH);
}

export const supportRouter = Router();
export const adminSupportRouter = Router();

supportRouter.use(requireAuth);
adminSupportRouter.use(requireAuth, requireAdmin);

supportRouter.get("/support/conversation", async (req: AuthenticatedRequest, res) => {
  if (req.user?.role === "admin") {
    return res.status(403).json({ message: "Используйте админский раздел обращений" });
  }

  const conversation = await ensureUserConversation(req.user!.id, extractEmail(req));
  await prisma.supportConversation.update({
    where: { id: conversation.id },
    data: {
      userLastReadAt: new Date()
    }
  });

  res.json({
    ...mapConversation(conversation),
    unreadForUser: false
  });
});

supportRouter.post("/support/conversation/messages", async (req: AuthenticatedRequest, res) => {
  if (req.user?.role === "admin") {
    return res.status(403).json({ message: "Администратор не может писать от имени пользователя" });
  }

  const body = extractBody(req);
  if (!body) {
    return res.status(400).json({ message: "Сообщение не должно быть пустым" });
  }

  const conversation = await ensureUserConversation(req.user!.id, extractEmail(req));
  const updated = await createSupportMessage({
    conversationId: conversation.id,
    authorId: req.user!.id,
    authorEmail: extractEmail(req),
    authorRole: UserRole.user,
    body
  });

  const payload = mapConversation(updated);
  const message = payload.messages?.at(-1) ?? payload.latestMessage ?? undefined;
  emitSupportConversationUpdated(updated.userId, { conversation: payload, message });
  emitSupportMessageCreated(updated.userId, { conversation: payload, message });

  res.status(201).json(payload);
});

adminSupportRouter.get("/support/conversations", async (_req, res) => {
  const conversations = await prisma.supportConversation.findMany({
    include: conversationListInclude,
    orderBy: [{ lastMessageAt: "desc" }, { createdAt: "desc" }]
  });

  res.json(conversations.map((conversation) => mapConversation(conversation)));
});

adminSupportRouter.get("/support/conversations/:id", async (req, res) => {
  const conversation = await prisma.supportConversation.findUnique({
    where: { id: String(req.params.id) },
    include: conversationDetailsInclude
  });

  if (!conversation) {
    return res.status(404).json({ message: "Диалог не найден" });
  }

  await prisma.supportConversation.update({
    where: { id: conversation.id },
    data: {
      adminLastReadAt: new Date()
    }
  });

  res.json({
    ...mapConversation(conversation),
    unreadForAdmin: false
  });
});

adminSupportRouter.post("/support/conversations/:id/messages", async (req: AuthenticatedRequest, res) => {
  const body = extractBody(req);
  if (!body) {
    return res.status(400).json({ message: "Сообщение не должно быть пустым" });
  }

  const conversation = await prisma.supportConversation.findUnique({
    where: { id: String(req.params.id) }
  });

  if (!conversation) {
    return res.status(404).json({ message: "Диалог не найден" });
  }

  const updated = await createSupportMessage({
    conversationId: conversation.id,
    authorId: req.user!.id,
    authorEmail: extractEmail(req),
    authorRole: UserRole.admin,
    body
  });

  const payload = mapConversation(updated);
  const message = payload.messages?.at(-1) ?? payload.latestMessage ?? undefined;
  emitSupportConversationUpdated(updated.userId, { conversation: payload, message });
  emitSupportMessageCreated(updated.userId, { conversation: payload, message });

  res.status(201).json(payload);
});

adminSupportRouter.patch("/support/conversations/:id", async (req: AuthenticatedRequest, res) => {
  const status =
    req.body?.status === SupportConversationStatus.closed ? SupportConversationStatus.closed : SupportConversationStatus.open;

  const updated = await prisma.supportConversation.update({
    where: { id: String(req.params.id) },
    data: {
      status,
      assignedAdminId: req.user!.id,
      assignedAdminEmail: extractEmail(req)
    },
    include: conversationListInclude
  });

  const payload = mapConversation(updated);
  emitSupportConversationUpdated(updated.userId, { conversation: payload });

  res.json(payload);
});
