import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';

import { withAuth } from '@/lib/api/helpers';
import { parseBody, parseQuery } from '@/lib/api/route-helpers';
import { errorResponse, NotFoundError, ValidationError } from '@/lib/errors';
import { requirePermission } from '@/lib/auth/permissions';
import { setValuesSchema } from '@/lib/validators/custom-fields';
import { getValues, setValues } from '@/lib/services/custom-fields.service';

/**
 * Custom-field values live on top of a port-scoped entity (client, yacht,
 * interest, berth, company). The previous implementation hardcoded the
 * permission check on `clients.view` / `clients.edit`, which let a user
 * with only `clients.view` read company-scoped custom-field values
 * through this endpoint. We now require an `entityType` query param so
 * the gate can match the entity's own resource permission.
 *
 * The service-layer also re-validates the entityId against each field
 * definition's entityType + portId so a caller cannot poke values onto
 * an arbitrary or foreign-port entity.
 */

const ENTITY_TYPE_VALUES = ['client', 'interest', 'berth', 'yacht', 'company'] as const;
type EntityTypeQuery = (typeof ENTITY_TYPE_VALUES)[number];

const querySchema = z.object({
  entityType: z.enum(ENTITY_TYPE_VALUES),
});

function gateForView(
  entityType: EntityTypeQuery,
  ctx: Parameters<typeof requirePermission>[0],
): void {
  switch (entityType) {
    case 'client':
      return requirePermission(ctx, 'clients', 'view');
    case 'interest':
      return requirePermission(ctx, 'interests', 'view');
    case 'berth':
      return requirePermission(ctx, 'berths', 'view');
    case 'yacht':
      return requirePermission(ctx, 'yachts', 'view');
    case 'company':
      return requirePermission(ctx, 'companies', 'view');
    default:
      throw new ValidationError(`Unsupported entityType: ${entityType as string}`);
  }
}

function gateForEdit(
  entityType: EntityTypeQuery,
  ctx: Parameters<typeof requirePermission>[0],
): void {
  switch (entityType) {
    case 'client':
      return requirePermission(ctx, 'clients', 'edit');
    case 'interest':
      return requirePermission(ctx, 'interests', 'edit');
    case 'berth':
      return requirePermission(ctx, 'berths', 'edit');
    case 'yacht':
      return requirePermission(ctx, 'yachts', 'edit');
    case 'company':
      return requirePermission(ctx, 'companies', 'edit');
    default:
      throw new ValidationError(`Unsupported entityType: ${entityType as string}`);
  }
}

export const GET = withAuth(async (req: NextRequest, ctx, params) => {
  try {
    const { entityId } = params;
    if (!entityId) throw new NotFoundError('Entity');

    const { entityType } = parseQuery(req, querySchema);
    gateForView(entityType, ctx);

    const data = await getValues(entityId, ctx.portId);
    return NextResponse.json({ data });
  } catch (error) {
    return errorResponse(error);
  }
});

export const PUT = withAuth(async (req: NextRequest, ctx, params) => {
  try {
    const { entityId } = params;
    if (!entityId) throw new NotFoundError('Entity');

    const { entityType } = parseQuery(req, querySchema);
    gateForEdit(entityType, ctx);

    const { values } = await parseBody(req, setValuesSchema);

    const result = await setValues(
      entityId,
      ctx.portId,
      ctx.userId,
      values as Array<{ fieldId: string; value: unknown }>,
      {
        userId: ctx.userId,
        portId: ctx.portId,
        ipAddress: ctx.ipAddress,
        userAgent: ctx.userAgent,
      },
    );

    return NextResponse.json({ data: result });
  } catch (error) {
    return errorResponse(error);
  }
});
