import { db } from "@/app/db/db";
import { NextRequest, NextResponse } from "next/server";
import { portfolioTag, propertyTag } from "@/app/db/schema/tags";
import { eq, sql } from "drizzle-orm";
import { z } from "zod";
import { getServerSession } from "next-auth";
import { AuthOptions } from "@/app/api/auth/[...nextauth]/authOptions";
import { validateTagDraft } from "@/lib/tags/model";
import {
  canReadTags,
  canWriteTags,
  getPortfolioRole,
  isUniqueViolation,
} from "@/lib/tags/server";

const createSchema = z.object({
  name: z.string(),
  colour: z.string(),
});

/** GET — the portfolio's tags with their property counts (ADR-0013). */
export async function GET(
  _req: NextRequest,
  props: { params: Promise<{ portfolioId: string }> },
) {
  const session = await getServerSession(AuthOptions);
  if (!session?.user?.email) {
    return NextResponse.json({ error: "Unauthorised" }, { status: 401 });
  }
  const { portfolioId } = await props.params;
  if (!/^\d+$/.test(portfolioId)) {
    return NextResponse.json({ error: "Invalid portfolio" }, { status: 400 });
  }
  const pId = BigInt(portfolioId);

  const role = await getPortfolioRole(pId, session.user.email);
  if (!canReadTags(role)) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const rows = await db
    .select({
      id: portfolioTag.id,
      name: portfolioTag.name,
      colour: portfolioTag.colour,
      propertyCount: sql<number>`count(${propertyTag.propertyId})::int`,
    })
    .from(portfolioTag)
    .leftJoin(propertyTag, eq(propertyTag.tagId, portfolioTag.id))
    .where(eq(portfolioTag.portfolioId, pId))
    .groupBy(portfolioTag.id)
    .orderBy(portfolioTag.name);

  return NextResponse.json(
    rows.map((r) => ({
      id: r.id.toString(),
      name: r.name,
      colour: r.colour,
      propertyCount: r.propertyCount,
    })),
  );
}

/** POST — create a tag (write-gated). */
export async function POST(
  req: NextRequest,
  props: { params: Promise<{ portfolioId: string }> },
) {
  const session = await getServerSession(AuthOptions);
  if (!session?.user?.email) {
    return NextResponse.json({ error: "Unauthorised" }, { status: 401 });
  }
  const { portfolioId } = await props.params;
  if (!/^\d+$/.test(portfolioId)) {
    return NextResponse.json({ error: "Invalid portfolio" }, { status: 400 });
  }
  const pId = BigInt(portfolioId);

  const role = await getPortfolioRole(pId, session.user.email);
  if (!canWriteTags(role)) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  let body: z.infer<typeof createSchema>;
  try {
    body = createSchema.parse(await req.json());
  } catch {
    return NextResponse.json({ error: "Invalid body" }, { status: 400 });
  }

  const existing = await db
    .select({ name: portfolioTag.name })
    .from(portfolioTag)
    .where(eq(portfolioTag.portfolioId, pId));

  const result = validateTagDraft(body, existing.map((e) => e.name));
  if (!result.ok) {
    return NextResponse.json({ error: result.error }, { status: 400 });
  }

  try {
    const [inserted] = await db
      .insert(portfolioTag)
      .values({
        portfolioId: pId,
        name: result.tag.name,
        colour: result.tag.colour,
      })
      .returning({ id: portfolioTag.id });
    return NextResponse.json(
      {
        id: inserted.id.toString(),
        name: result.tag.name,
        colour: result.tag.colour,
        propertyCount: 0,
      },
      { status: 201 },
    );
  } catch (err) {
    // Only the case-insensitive unique index racing a concurrent create is a
    // 409; anything else (connection drop, other constraint) is a real failure
    // that must not be masked as a duplicate-name.
    if (isUniqueViolation(err)) {
      return NextResponse.json(
        { error: "A tag with this name already exists" },
        { status: 409 },
      );
    }
    console.error("POST /tags insert error:", err);
    return NextResponse.json(
      { error: "Couldn't create the tag" },
      { status: 500 },
    );
  }
}
