import { NextResponse } from 'next/server';
import { and, eq, isNull } from 'drizzle-orm';

import { db } from '@/lib/db';
import { ports } from '@/lib/db/schema/ports';
import { berths, berthMapData } from '@/lib/db/schema/berths';
import { interestBerths, interests } from '@/lib/db/schema/interests';
import { berthTenancies } from '@/lib/db/schema/tenancies';
import { errorResponse } from '@/lib/errors';
import { logger } from '@/lib/logger';
import { isTenanciesModuleEnabled } from '@/lib/services/tenancies-module.service';
import { isPermanentTenureType, toPublicBerth } from '@/lib/services/public-berths';

/**
 * GET /api/public/berths/[mooringNumber]
 *
 * Single-berth lookup for the public website's `/berths/[number]`
 * page. Mooring numbers are matched against the canonical bare form
 * ("A1", "B12") - Phase 0 normalized the entire CRM dataset.
 */

// Hard-coded allowlist for the public read-only feed. Adding a port here
// is a deliberate decision (not silent enumeration via ?portSlug=), so a
// future private tenant can't be exposed by accident.
const PUBLIC_PORT_SLUGS = new Set(['port-nimara']);
const DEFAULT_PUBLIC_PORT_SLUG = 'port-nimara';
const RESPONSE_HEADERS = {
  'cache-control': 'public, s-maxage=300, stale-while-revalidate=60',
  'content-type': 'application/json; charset=utf-8',
};

const MOORING_PATTERN = /^[A-Z]+\d+$/;

export async function GET(
  request: Request,
  ctx: { params: Promise<{ mooringNumber: string }> },
): Promise<Response> {
  try {
    const { mooringNumber } = await ctx.params;
    const url = new URL(request.url);
    const requestedSlug = url.searchParams.get('portSlug') ?? DEFAULT_PUBLIC_PORT_SLUG;
    if (!PUBLIC_PORT_SLUGS.has(requestedSlug)) {
      return NextResponse.json(
        { error: 'port is not part of the public berths feed', portSlug: requestedSlug },
        { status: 404, headers: { 'cache-control': 'no-store' } },
      );
    }
    const portSlug = requestedSlug;

    // Reject obviously malformed mooring numbers up front so cache poisoning
    // / random-URL probing returns 400 rather than 404 (saves a DB hit).
    if (!MOORING_PATTERN.test(mooringNumber)) {
      return NextResponse.json(
        { error: 'invalid mooring number', mooringNumber },
        { status: 400, headers: { 'cache-control': 'no-store' } },
      );
    }

    const [port] = await db
      .select({ id: ports.id })
      .from(ports)
      .where(eq(ports.slug, portSlug))
      .limit(1);
    if (!port) {
      return NextResponse.json(
        { error: 'port not found', portSlug },
        { status: 404, headers: { 'cache-control': 'no-store' } },
      );
    }

    const [berth] = await db
      .select()
      .from(berths)
      .where(
        and(
          eq(berths.portId, port.id),
          eq(berths.mooringNumber, mooringNumber),
          isNull(berths.archivedAt),
        ),
      )
      .limit(1);

    if (!berth) {
      return NextResponse.json(
        { error: 'berth not found', mooringNumber },
        { status: 404, headers: { 'cache-control': 'no-store' } },
      );
    }

    const [mapData, specificInterestRows] = await Promise.all([
      db.select().from(berthMapData).where(eq(berthMapData.berthId, berth.id)).limit(1),
      db
        .select({ berthId: interestBerths.berthId })
        .from(interestBerths)
        .innerJoin(interests, eq(interests.id, interestBerths.interestId))
        .where(
          and(
            eq(interestBerths.berthId, berth.id),
            eq(interestBerths.isSpecificInterest, true),
            isNull(interests.archivedAt),
            // Closed deals (won/lost/cancelled) don't promote to "Under
            // Offer" - won flows through berths.status='sold' handled in
            // derivePublicStatus; lost/cancelled means back on the market.
            isNull(interests.outcome),
          ),
        )
        .limit(1),
    ]);

    // Tenancies-module status flip: an active permanent-class tenancy
    // pushes the berth to "Sold" when the module is enabled for this port.
    let hasActivePermanentTenancy = false;
    if (await isTenanciesModuleEnabled(port.id)) {
      const activeTenancy = await db
        .select({ tenureType: berthTenancies.tenureType })
        .from(berthTenancies)
        .where(
          and(
            eq(berthTenancies.portId, port.id),
            eq(berthTenancies.berthId, berth.id),
            eq(berthTenancies.status, 'active'),
          ),
        )
        .limit(1);
      hasActivePermanentTenancy = activeTenancy.some((row) =>
        isPermanentTenureType(row.tenureType),
      );
    }

    const out = toPublicBerth(
      berth,
      mapData[0] ?? null,
      specificInterestRows.length > 0,
      hasActivePermanentTenancy,
    );

    if (out.Status !== 'Available' && out.Status !== 'Under Offer' && out.Status !== 'Sold') {
      logger.error({ berthId: berth.id, status: out.Status }, 'Public berth status out of range');
      throw new Error('Public berth status out of range');
    }

    return new Response(JSON.stringify(out), { headers: RESPONSE_HEADERS, status: 200 });
  } catch (err) {
    return errorResponse(err);
  }
}
