import { db } from "@/app/db/db";
import { NextRequest, NextResponse } from "next/server";
import { portfolio, portfolioUsers } from "@/app/db/schema/portfolio";
import { user } from "@/app/db/schema/users";
import {
  recommendation,
  recommendationMaterials,
  planRecommendations,
  plan,
  scenario,
} from "@/app/db/schema/recommendations";
import {
  propertyTargets,
  propertyDetailsEpc,
  property,
} from "@/app/db/schema/property";
import { eq, inArray, Name } from "drizzle-orm";
import { z } from "zod";
import { ROLE_OPTIONS } from "@/app/portfolio/[slug]/(portfolio)/settings/roles";

// Get colloborators (users) that have access to the portfolio
export async function GET(
  _req: NextRequest,
  props: { params: Promise<{ portfolioId: string }> }
) {
  const { portfolioId } = await props.params;

  try {
    const rows = await db
      .select({
        portfolioUserId: portfolioUsers.id,
        userId: portfolioUsers.userId,
        role: portfolioUsers.role,
        name: user.firstName,
        email: user.email,
      })
      .from(portfolioUsers)
      .leftJoin(user, eq(user.id, portfolioUsers.userId))
      .where(eq(portfolioUsers.portfolioId, BigInt(portfolioId)));

    // Explicitly normalize BigInts to strings
    const collaborators = rows.map((r) => ({
      portfolioUserId: r.portfolioUserId ? r.portfolioUserId.toString() : null,
      userId: r.userId ? r.userId.toString() : null,
      role: r.role,
      name: r.name ?? null,
      email: r.email ?? "",
    }));

    return NextResponse.json({ users: collaborators }, { status: 200 });
  } catch (err) {
    console.error("GET /users error:", err);
    return NextResponse.json(
      { error: "Failed to fetch users" },
      { status: 500 }
    );
  }
}

// PUT: update a collaborator’s role
export async function PUT(
  req: NextRequest,
  props: { params: Promise<{ portfolioId: string }> }
) {
  const { portfolioId } = await props.params;

  // Validate request body
  const bodySchema = z.object({
    portfolioUserId: z.string(),
    role: z.enum(ROLE_OPTIONS), // adjust to your Role union
  });

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

  try {
    // Update role for this portfolioUserId
    await db
      .update(portfolioUsers)
      .set({ role: body.role })
      .where(eq(portfolioUsers.id, BigInt(body.portfolioUserId)));

    return NextResponse.json(
      { success: true, portfolioUserId: body.portfolioUserId, role: body.role },
      { status: 200 }
    );
  } catch (err) {
    console.error("PUT /colloborators error:", err);
    return NextResponse.json(
      { error: "Failed to update role" },
      { status: 500 }
    );
  }
}

// POST: invite a user by email (find-or-create user, then add to portfolio with role)
export async function POST(
  req: NextRequest,
  props: { params: Promise<{ portfolioId: string }> }
) {
  const { portfolioId } = await props.params;

  // 1) Validate payload
  const bodySchema = z.object({
    email: z.string().email(),
    role: z.enum(ROLE_OPTIONS),
    name: z.string(),
  });

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

  try {
    const pId = BigInt(portfolioId);

    // 2) Find or create the user by email
    // Try to find existing user
    let existing = await db
      .select({ id: user.id, firstName: user.firstName, email: user.email })
      .from(user)
      .where(eq(user.email, body.email))
      .limit(1);

    let createdUserId: bigint | null = existing[0]?.id ?? null;

    // If not found, create. Prefer Postgres upsert to avoid race.
    if (!createdUserId) {
      // If you’re on Postgres, this is ideal:
      const inserted = await db
        .insert(user)
        .values({
          email: body.email,
          firstName: body.name,
          oauthProvider: "credentials",
        })
        .onConflictDoNothing() // relies on a UNIQUE(email) constraint
        .returning({ id: user.id });

      if (inserted.length > 0) {
        createdUserId = inserted[0].id;
      } else {
        // Someone else created the user concurrently; fetch it
        const fetched = await db
          .select({ id: user.id })
          .from(user)
          .where(eq(user.email, body.email))
          .limit(1);
        if (!fetched[0]) {
          return NextResponse.json(
            { error: "Failed to create or fetch user" },
            { status: 500 }
          );
        }
        createdUserId = fetched[0].id;
      }
    }

    // 3) Link user to portfolio with role (upsert)
    // Assumes a UNIQUE index on (portfolioId, userId) in portfolioUsers.
    const linkResult = await db
      .insert(portfolioUsers)
      .values({
        portfolioId: pId,
        userId: createdUserId!,
        role: body.role,
      })
      .returning({
        portfolioUserId: portfolioUsers.id,
        userId: portfolioUsers.userId,
        role: portfolioUsers.role,
      });

    const row = linkResult[0];
    if (!row) {
      return NextResponse.json(
        { error: "Failed to create portfolio user" },
        { status: 500 }
      );
    }

    const collaborator = {
      portfolioUserId: row.portfolioUserId?.toString() ?? null,
      userId: row.userId?.toString() ?? null,
      role: row.role,
      name: body.name ?? null,
      email: body.email,
    };

    // 201 if it was a new link, 200 if it was an update — we can’t easily
    // tell from .onConflictDoUpdate return, so just use 200 OK.
    return NextResponse.json({ user: collaborator }, { status: 200 });
  } catch (err) {
    console.error("POST /colloborators error:", err);
    return NextResponse.json(
      { error: "Failed to invite user" },
      { status: 500 }
    );
  }
}
