import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { db, conversations, conversationReads, messages, users, eq, or, desc, and, gt, sql } from "@epicure/db";
import { requireSession } from "@/lib/api-auth";
import { applyRateLimit } from "@/lib/rate-limit";
import { findOrCreateConversation, otherParticipantId } from "@/lib/messaging";
import { isBlockedEitherWay } from "@/lib/blocks";
import { randomUUID } from "crypto";

const CreateSchema = z.object({
  username: z.string().min(1),
  content: z.string().min(1).max(4000).optional(),
});

export async function GET() {
  const { session, response } = await requireSession();
  if (response) return response;

  const rows = await db
    .select()
    .from(conversations)
    .where(or(eq(conversations.userAId, session!.user.id), eq(conversations.userBId, session!.user.id)))
    .orderBy(desc(conversations.lastMessageAt))
    .limit(50);

  const result = await Promise.all(
    rows.map(async (conv) => {
      const otherId = otherParticipantId(conv, session!.user.id);
      const [other, lastMessage, readRow] = await Promise.all([
        db.query.users.findFirst({
          where: eq(users.id, otherId),
          columns: { id: true, name: true, username: true, avatarUrl: true },
        }),
        db.query.messages.findFirst({
          where: eq(messages.conversationId, conv.id),
          orderBy: (m, { desc: d }) => [d(m.createdAt)],
        }),
        db.query.conversationReads.findFirst({
          where: and(eq(conversationReads.conversationId, conv.id), eq(conversationReads.userId, session!.user.id)),
        }),
      ]);

      const unreadRow = await db
        .select({ total: sql<string>`count(*)` })
        .from(messages)
        .where(
          and(
            eq(messages.conversationId, conv.id),
            readRow ? gt(messages.createdAt, readRow.lastReadAt) : sql`true`,
            sql`${messages.senderId} != ${session!.user.id}`
          )
        );

      return {
        id: conv.id,
        otherUser: other,
        lastMessage: lastMessage?.content ?? null,
        lastMessageAt: conv.lastMessageAt,
        unreadCount: Number(unreadRow[0]?.total ?? 0),
      };
    })
  );

  return NextResponse.json({ conversations: result });
}

export async function POST(req: NextRequest) {
  const { session, response } = await requireSession();
  if (response) return response;

  const limited = await applyRateLimit(`rl:message:${session!.user.id}`, 30, 60);
  if (limited) return limited;

  const body = await req.json() as unknown;
  const parsed = CreateSchema.safeParse(body);
  if (!parsed.success) return NextResponse.json({ error: "Validation error" }, { status: 400 });

  const target = await db.query.users.findFirst({ where: eq(users.username, parsed.data.username) });
  if (!target) return NextResponse.json({ error: "Not found" }, { status: 404 });
  if (target.id === session!.user.id) return NextResponse.json({ error: "Cannot message yourself" }, { status: 400 });

  if (await isBlockedEitherWay(session!.user.id, target.id)) {
    return NextResponse.json({ error: "Not found" }, { status: 404 });
  }

  const conversation = await findOrCreateConversation(session!.user.id, target.id);

  if (parsed.data.content) {
    await db.insert(messages).values({
      id: randomUUID(),
      conversationId: conversation.id,
      senderId: session!.user.id,
      content: parsed.data.content,
    });
    await db.update(conversations).set({ lastMessageAt: new Date() }).where(eq(conversations.id, conversation.id));
  }

  return NextResponse.json({ conversationId: conversation.id }, { status: 201 });
}
