import { NextResponse } from 'next/server';
import { withErrorHandler, RouteContext } from '@server/middleware/withErrorHandler';
import { withAuth, requireSection, AuthedRequest } from '@server/middleware/withAuth';
import { withValidationAuthed } from '@server/middleware/withValidation';
import { requirePlanFeature } from '@server/utils/features';
import { updateCampaignSchema, type UpdateCampaignInput } from '@server/validators/marketing.validator';
import { getCampaign, updateCampaign, deleteCampaign } from '@server/services/marketing/campaigns.service';
import { getSegment } from '@server/services/marketing/segments.service';
import { effectiveBranchId, resolveWritableBranchId, scopeAudienceRules, loadAccessibleOrThrow } from '@server/utils/branch-access';

/**
 * Pre-flight: confirm the caller can see this campaign at all under their
 * effective branch scope. Mutations below trust this check — without it
 * a pinned-staff user could PUT/DELETE/launch a sibling branch's campaign
 * by guessing its UUID, since the underlying service helpers only filter
 * on restaurant_id.
 *
 * Uses loadAccessibleOrThrow so sibling-branch IDs return an explicit 403
 * (rather than the same 404 a truly missing UUID would return), matching
 * the contract the branches API uses.
 */
async function loadAccessible(req: AuthedRequest, id: string) {
  const restaurantId = req.session.restaurantId!;
  return loadAccessibleOrThrow(
    await getCampaign(restaurantId, id, effectiveBranchId(req.session)),
    () => getCampaign(restaurantId, id, null),
    'Campaign',
  );
}

export const GET = withErrorHandler(
  withAuth(async (req: AuthedRequest, ctx: RouteContext) => {
    const restaurantId = req.session.restaurantId!;
    await requireSection(req, 'marketing');
    await requirePlanFeature(restaurantId, 'marketing');
    const { id } = await ctx.params;
    const campaign = await loadAccessible(req, id);
    return NextResponse.json({ campaign });
  })
);

export const PUT = withErrorHandler(
  withAuth(
    withValidationAuthed(updateCampaignSchema, async (req, ctx: RouteContext) => {
      const restaurantId = req.session.restaurantId!;
      await requireSection(req, 'marketing', 'update');
      await requirePlanFeature(restaurantId, 'marketing');
      const { id } = await ctx.params;
      await loadAccessible(req, id);
      const body = req.parsedBody as UpdateCampaignInput;
      // Re-resolve branchId so a pinned user can't repoint the campaign at
      // a sibling branch via PUT. Owners may move it freely.
      let patch: UpdateCampaignInput = body.branchId !== undefined
        ? { ...body, branchId: resolveWritableBranchId(req.session, body.branchId) }
        : body;
      // Same defence on audienceRules.branchIds as on POST — without it
      // a pinned user could PATCH the rules JSON to target sibling
      // customers even though loadAccessible already proved the campaign
      // belongs to their branch.
      if (patch.audienceRules !== undefined) {
        patch = { ...patch, audienceRules: scopeAudienceRules(req.session, patch.audienceRules) };
      }
      // If the PATCH points segmentId at a different segment, validate
      // it the same way as POST — otherwise a pinned user could swap in
      // a sibling segment's UUID and inherit its rules.
      if (patch.segmentId) {
        await loadAccessibleOrThrow(
          await getSegment(restaurantId, patch.segmentId, effectiveBranchId(req.session)),
          () => getSegment(restaurantId, patch.segmentId!, null),
          'Segment',
        );
      }
      const campaign = await updateCampaign(restaurantId, id, patch);
      return NextResponse.json({ campaign });
    })
  )
);

export const DELETE = withErrorHandler(
  withAuth(async (req: AuthedRequest, ctx: RouteContext) => {
    const restaurantId = req.session.restaurantId!;
    await requireSection(req, 'marketing', 'delete');
    await requirePlanFeature(restaurantId, 'marketing');
    const { id } = await ctx.params;
    await loadAccessible(req, id);
    await deleteCampaign(restaurantId, id);
    return NextResponse.json({ success: true });
  })
);
