import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { and, eq } from "drizzle-orm";
import { z } from "zod";

import { AuthOptions } from "@/app/api/auth/[...nextauth]/authOptions";
import { db } from "@/app/db/db";
import {
  portfolioCapabilities,
  portfolioUsers,
} from "@/app/db/schema/portfolio";
import { user } from "@/app/db/schema/users";
import { userDefinedDealMeasures } from "@/app/db/schema/user_defined_deal_measures";
import { dealMeasureApprovals } from "@/app/db/schema/approvals";
import { selectPibiMeasures } from "@/app/lib/selectPibiMeasures";

const postSchema = z.object({
  dealId: z.string().min(1, "dealId is required"),
  measureNames: z.array(z.string()).min(0),
});

/**
 * GET /api/portfolio/[portfolioId]/pibi-measures?dealId=...
 *
 * Returns the current PIBI selection and approved measure names for a deal.
 * Used by the drawer's PIBI selector to pre-populate the multi-select.
 *
 * Response:
 *   200 { pibiMeasures: string[], approvedMeasures: string[], instructedMeasures: string[] }
 */
export async function GET(
  req: NextRequest,
  props: { params: Promise<{ portfolioId: string }> },
) {
  const { portfolioId } = await props.params;

  const session = await getServerSession(AuthOptions);
  if (!session?.user?.email) {
    return NextResponse.json({ error: "Unauthorised" }, { status: 401 });
  }

  const dealId = req.nextUrl.searchParams.get("dealId");
  if (!dealId) {
    return NextResponse.json(
      { error: "dealId query param is required" },
      { status: 400 },
    );
  }

  const userRow = await db
    .select({ id: user.id })
    .from(user)
    .where(eq(user.email, session.user.email))
    .limit(1);

  if (!userRow[0]) {
    return NextResponse.json({ error: "User not found" }, { status: 404 });
  }

  const portfolioUserRow = await db
    .select({ role: portfolioUsers.role })
    .from(portfolioUsers)
    .where(
      and(
        eq(portfolioUsers.portfolioId, BigInt(portfolioId)),
        eq(portfolioUsers.userId, userRow[0].id),
      ),
    )
    .limit(1);

  if (!portfolioUserRow[0]?.role) {
    return NextResponse.json(
      { error: "No portfolio access" },
      { status: 403 },
    );
  }

  const [pibiRows, approvalRows, instructedRows] = await Promise.all([
    db
      .select({ measureName: userDefinedDealMeasures.measureName })
      .from(userDefinedDealMeasures)
      .where(
        and(
          eq(userDefinedDealMeasures.hubspotDealId, dealId),
          eq(userDefinedDealMeasures.source, "pibi_ordered"),
        ),
      ),
    db
      .select({ measureName: dealMeasureApprovals.measureName })
      .from(dealMeasureApprovals)
      .where(
        and(
          eq(dealMeasureApprovals.hubspotDealId, dealId),
          eq(dealMeasureApprovals.isApproved, true),
        ),
      ),
    db
      .select({ measureName: userDefinedDealMeasures.measureName })
      .from(userDefinedDealMeasures)
      .where(
        and(
          eq(userDefinedDealMeasures.hubspotDealId, dealId),
          eq(userDefinedDealMeasures.source, "instructed"),
        ),
      ),
  ]);

  return NextResponse.json({
    pibiMeasures: pibiRows.map((r) => r.measureName),
    approvedMeasures: approvalRows.map((r) => r.measureName),
    instructedMeasures: instructedRows.map((r) => r.measureName),
  });
}

/**
 * POST /api/portfolio/[portfolioId]/pibi-measures
 *
 * Approver-only endpoint that records which measures on a deal are going for
 * PIBI. The incoming `measureNames[]` is the FULL desired set — it replaces
 * any prior selection. Persists to `user_defined_deal_measures` with
 * `source = "pibi_ordered"` and pushes back to HubSpot under
 * `measures_for_pibi_ordered`. See `selectPibiMeasures` for the full
 * contract.
 *
 * Body:
 *   { dealId: string, measureNames: string[] }
 *
 * Response:
 *   200 { ok: true, hubspotSync: "ok" | "failed", hubspotError? }
 *   400 { ok: false, error }
 *   401 / 403 / 404 on auth/role/user errors.
 */
export async function POST(
  req: NextRequest,
  props: { params: Promise<{ portfolioId: string }> },
) {
  const { portfolioId } = await props.params;

  const session = await getServerSession(AuthOptions);
  if (!session?.user?.email) {
    return NextResponse.json({ error: "Unauthorised" }, { status: 401 });
  }

  let body: unknown;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
  }

  const parsed = postSchema.safeParse(body);
  if (!parsed.success) {
    return NextResponse.json(
      { error: parsed.error.flatten() },
      { status: 400 },
    );
  }

  const { dealId, measureNames } = parsed.data;

  const userRow = await db
    .select({ id: user.id })
    .from(user)
    .where(eq(user.email, session.user.email))
    .limit(1);

  if (!userRow[0]) {
    return NextResponse.json({ error: "User not found" }, { status: 404 });
  }

  // Caller must be a portfolio member AND have the approver capability.
  const portfolioUserRow = await db
    .select({ role: portfolioUsers.role })
    .from(portfolioUsers)
    .where(
      and(
        eq(portfolioUsers.portfolioId, BigInt(portfolioId)),
        eq(portfolioUsers.userId, userRow[0].id),
      ),
    )
    .limit(1);

  if (!portfolioUserRow[0]?.role) {
    return NextResponse.json(
      { error: "No portfolio access" },
      { status: 403 },
    );
  }

  const capabilityRows = await db
    .select({ capability: portfolioCapabilities.capability })
    .from(portfolioCapabilities)
    .where(
      and(
        eq(portfolioCapabilities.portfolioId, BigInt(portfolioId)),
        eq(portfolioCapabilities.userId, userRow[0].id),
      ),
    );
  const capabilities = capabilityRows.map((r) => r.capability);
  if (!capabilities.includes("approver")) {
    return NextResponse.json(
      { error: "Approver capability required" },
      { status: 403 },
    );
  }

  try {
    const result = await selectPibiMeasures({
      dealId,
      measureNames,
      userId: userRow[0].id,
    });

    if (!result.ok) {
      return NextResponse.json({ ok: false, error: result.error }, {
        status: 400,
      });
    }
    return NextResponse.json({
      ok: true,
      hubspotSync: result.hubspotSync,
      hubspotError: result.hubspotError,
    });
  } catch (err) {
    console.error("[pibi-measures POST]", err);
    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 },
    );
  }
}
