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

import { withAuth, withRateLimit } from '@/lib/api/helpers';
import { parseBody } from '@/lib/api/route-helpers';
import { errorResponse } from '@/lib/errors';
import { assertResidentialModuleEnabled } from '@/lib/services/residential-module.service';
import { assertValidStage } from '@/lib/services/residential-stages.service';
import {
  archiveResidentialInterest,
  updateResidentialInterest,
} from '@/lib/services/residential.service';

/**
 * Synchronous bulk endpoint for the residential interests list - mirrors
 * the `/api/v1/interests/bulk` shape (and the new `/api/v1/berths/bulk`)
 * so the rep-facing UX is consistent. Per-row loop with a 100-id cap.
 *
 * Permission gating: every action requires `residential_interests.edit`
 * except `archive` which needs `residential_interests.delete` (mirrors
 * the per-row endpoints' gates).
 */
const bulkSchema = z.discriminatedUnion('action', [
  z.object({
    action: z.literal('change_stage'),
    ids: z.array(z.string().min(1)).min(1).max(100),
    // Accept any non-empty string at the schema layer; membership against
    // the port's live stage list (built-ins OR admin-customized) is
    // enforced at runtime via `assertValidStage` inside the handler. A
    // hardcoded enum here would 400 every valid custom stage after an
    // admin renames/adds stages, while the per-row PATCH wrote arbitrary
    // strings unchecked — this unifies both on one runtime check.
    pipelineStage: z.string().min(1),
  }),
  z.object({
    action: z.literal('archive'),
    ids: z.array(z.string().min(1)).min(1).max(100),
  }),
]);

interface RowResult {
  id: string;
  ok: boolean;
  error?: string;
}

const PERMISSION_BY_ACTION: Record<
  z.infer<typeof bulkSchema>['action'],
  { resource: 'residential_interests'; action: 'edit' | 'delete' }
> = {
  change_stage: { resource: 'residential_interests', action: 'edit' },
  archive: { resource: 'residential_interests', action: 'delete' },
};

export const POST = withAuth(
  withRateLimit('bulk', async (req, ctx) => {
    let body: z.infer<typeof bulkSchema>;
    try {
      await assertResidentialModuleEnabled(ctx.portId);
      body = await parseBody(req, bulkSchema);
    } catch (error) {
      return errorResponse(error);
    }

    const perm = PERMISSION_BY_ACTION[body.action];
    const allowed = ctx.isSuperAdmin ? true : !!ctx.permissions?.[perm.resource]?.[perm.action];
    if (!allowed) {
      return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
    }

    // Validate the target stage once up-front against the port's live
    // stage list so an invalid stage 400s the whole request instead of
    // reporting every row as failed. `updateResidentialInterest` also
    // re-checks per row (defense in depth for direct callers).
    if (body.action === 'change_stage') {
      try {
        await assertValidStage(ctx.portId, body.pipelineStage);
      } catch (error) {
        return errorResponse(error);
      }
    }

    const meta = {
      userId: ctx.userId,
      portId: ctx.portId,
      ipAddress: ctx.ipAddress,
      userAgent: ctx.userAgent,
    };

    const results: RowResult[] = [];

    for (const id of body.ids) {
      try {
        if (body.action === 'change_stage') {
          await updateResidentialInterest(
            id,
            ctx.portId,
            { pipelineStage: body.pipelineStage },
            meta,
          );
        } else if (body.action === 'archive') {
          await archiveResidentialInterest(id, ctx.portId, meta);
        }
        results.push({ id, ok: true });
      } catch (err) {
        const message = err instanceof Error ? err.message : 'Unknown error';
        results.push({ id, ok: false, error: message });
      }
    }

    const okCount = results.filter((r) => r.ok).length;
    return NextResponse.json({
      data: {
        action: body.action,
        total: results.length,
        ok: okCount,
        failed: results.length - okCount,
        results,
        summary: {
          total: results.length,
          succeeded: okCount,
          failed: results.length - okCount,
        },
      },
    });
  }),
);
