import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { getCurrentUser } from "@/lib/auth";
import { brandAccessByUserInOrg } from "@/lib/brand-access";
import { PLANS, resolveEffectivePlan, type PlanId } from "@/lib/plan-config";

/**
 * GET /api/organizations/members?orgId=xxx
 * Returns all members of an organization, with the list of brandIds
 * each member has explicit BrandMembership access to. Owners/admins
 * implicitly see every brand — brandAccess still returns their
 * explicit grants (usually empty) so the UI can distinguish
 * "implicit-via-role" from "explicit-via-invite".
 */
export const dynamic = "force-dynamic";

export async function GET(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { searchParams } = new URL(req.url);
  const orgId = searchParams.get("orgId");

  if (!orgId) {
    return NextResponse.json({ error: "orgId is required" }, { status: 400 });
  }

  // Verify user is a member
  const membership = await prisma.orgMembership.findUnique({
    where: { userId_orgId: { userId: user.id, orgId } },
  });

  if (!membership) {
    return NextResponse.json({ error: "Not a member of this organization" }, { status: 403 });
  }

  const [members, brandAccessMap] = await Promise.all([
    prisma.orgMembership.findMany({
      where: { orgId },
      include: {
        user: { select: { id: true, name: true, email: true, image: true } },
      },
      orderBy: { createdAt: "asc" },
    }),
    brandAccessByUserInOrg(orgId),
  ]);

  return NextResponse.json(
    members.map((m) => ({
      id: m.id,
      role: m.role,
      joinedAt: m.createdAt,
      user: m.user,
      brandAccess: brandAccessMap.get(m.user.id) ?? [],
    })),
  );
}

/**
 * POST /api/organizations/members
 * Invite a user to an organization (by email).
 * Body: { orgId, email, role? }
 */
export async function POST(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  try {
    const { orgId, email, role, brandIds } = await req.json() as {
      orgId?: string; email?: string; role?: string;
      /** Optional list of brandIds to grant access to on invite.
       *  Owners/admins don't need this — they see every brand via
       *  role. Members get explicit BrandMemberships for each id. */
      brandIds?: string[];
    };

    if (!orgId || !email) {
      return NextResponse.json({ error: "orgId and email are required" }, { status: 400 });
    }

    // Verify the inviter is an owner or admin
    const inviterMembership = await prisma.orgMembership.findUnique({
      where: { userId_orgId: { userId: user.id, orgId } },
    });

    if (!inviterMembership || !["owner", "admin"].includes(inviterMembership.role)) {
      return NextResponse.json({ error: "Only owners and admins can invite members" }, { status: 403 });
    }

    // Seat-limit check — every brand in the org is meant to share
    // the same plan (1 brand = 1 subscription at the moment) so we
    // resolve the cap off the org's first brand. An org with zero
    // brands yet has no plan context; fall through to allow the
    // invite so the trial can be set up before a brand exists.
    const planBrand = await prisma.brand.findFirst({
      where: { orgId },
      select: { plan: true, planExpiresAt: true },
      orderBy: { createdAt: "asc" },
    });
    if (planBrand) {
      const effective = resolveEffectivePlan({
        plan: (planBrand.plan as PlanId) ?? "trial",
        planExpiresAt: planBrand.planExpiresAt,
      });
      const seatLimit = PLANS[effective].limits.seats;
      if (seatLimit !== "unlimited") {
        const current = await prisma.orgMembership.count({ where: { orgId } });
        if (current >= seatLimit) {
          return NextResponse.json(
            {
              error: "upgrade_required",
              reason: "seat_limit",
              message: `You're using ${current} of ${seatLimit} seats on ${PLANS[effective].name}. Upgrade for more seats or add a seat add-on.`,
              currentPlan: effective,
              limit: { used: current, max: seatLimit },
            },
            { status: 403 },
          );
        }
      }
    }

    // Find or create the invitee by email
    let invitee = await prisma.user.findUnique({ where: { email } });
    if (!invitee) {
      // Create a placeholder user — will be merged when they sign up via Clerk
      invitee = await prisma.user.create({
        data: { id: `pending_${Date.now().toString(36)}`, email, name: email.split("@")[0] },
      });
    }

    // Check if already a member
    const existing = await prisma.orgMembership.findUnique({
      where: { userId_orgId: { userId: invitee.id, orgId } },
    });

    if (existing) {
      return NextResponse.json({ error: "User is already a member" }, { status: 400 });
    }

    const membership = await prisma.orgMembership.create({
      data: {
        userId: invitee.id,
        orgId,
        role: role || "member",
      },
      include: {
        user: { select: { id: true, name: true, email: true, image: true } },
      },
    });

    // Per-brand grants. Restricted to brandIds that actually live in
    // this org so a bad payload can't cross-grant access to brands
    // outside the inviter's control. Owners/admins skip this — they
    // have role-based access to every brand anyway, and explicit
    // rows would only clutter the Team tab's Brand Access column.
    if (Array.isArray(brandIds) && brandIds.length > 0 && !["owner", "admin"].includes(membership.role)) {
      const validBrands = await prisma.brand.findMany({
        where: { orgId, id: { in: brandIds } },
        select: { id: true },
      });
      if (validBrands.length > 0) {
        await prisma.brandMembership.createMany({
          data: validBrands.map((b) => ({
            userId: invitee.id,
            brandId: b.id,
            role: role === "admin" ? "admin" : "member",
          })),
          skipDuplicates: true,
        });
      }
    }

    return NextResponse.json({
      id: membership.id,
      role: membership.role,
      user: membership.user,
    }, { status: 201 });

  } catch {
    return NextResponse.json({ error: "Something went wrong" }, { status: 500 });
  }
}

/**
 * PATCH /api/organizations/members
 * Update a member's role and/or per-brand access.
 * Body: { orgId, userId, role?, brandIds? }
 *   - role:      new OrgMembership.role
 *   - brandIds:  replaces the member's BrandMembership rows entirely.
 *                Pass [] to revoke all per-brand grants; omit to
 *                leave them untouched.
 */
export async function PATCH(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  try {
    const { orgId, userId, role, brandIds } = await req.json() as {
      orgId?: string; userId?: string; role?: string; brandIds?: string[];
    };
    if (!orgId || !userId) {
      return NextResponse.json({ error: "orgId and userId required" }, { status: 400 });
    }

    // Only owners/admins can edit memberships. Members editing their
    // own row would be a separate self-service flow.
    const editor = await prisma.orgMembership.findUnique({
      where: { userId_orgId: { userId: user.id, orgId } },
    });
    if (!editor || !["owner", "admin"].includes(editor.role)) {
      return NextResponse.json({ error: "Only owners and admins can edit members" }, { status: 403 });
    }

    // Role update.
    if (role) {
      // Guard: non-owners can't crown another owner. Only an owner
      // can promote/demote to owner.
      if (role === "owner" && editor.role !== "owner") {
        return NextResponse.json({ error: "Only owners can assign the owner role" }, { status: 403 });
      }
      await prisma.orgMembership.update({
        where: { userId_orgId: { userId, orgId } },
        data: { role },
      });
    }

    // Brand-access replacement. Scoped to brands in this org so a
    // stray id outside the org can't sneak in.
    if (Array.isArray(brandIds)) {
      const validBrands = await prisma.brand.findMany({
        where: { orgId, id: { in: brandIds } },
        select: { id: true },
      });
      const keepIds = validBrands.map((b) => b.id);

      // Delete then recreate is simpler than diffing when the set is
      // small (typical: a handful of brands per org). If this ever
      // becomes hot, swap to explicit diff + delete/insert.
      await prisma.brandMembership.deleteMany({
        where: { userId, brand: { orgId } },
      });
      if (keepIds.length > 0) {
        // Pick BrandMembership.role from the current OrgMembership
        // role so a downgraded admin → member also drops to member
        // on every brand they retained access to.
        const currentMembership = await prisma.orgMembership.findUnique({
          where: { userId_orgId: { userId, orgId } },
        });
        const brandRole = currentMembership?.role === "admin" ? "admin" : "member";
        await prisma.brandMembership.createMany({
          data: keepIds.map((id) => ({ userId, brandId: id, role: brandRole })),
        });
      }
    }

    return NextResponse.json({ ok: true });
  } catch {
    return NextResponse.json({ error: "Something went wrong" }, { status: 500 });
  }
}
