import { NextResponse } from 'next/server';
import { withErrorHandler } 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 { createCampaignSchema, type CreateCampaignInput } from '@server/validators/marketing.validator';
import { listCampaigns, createCampaign } from '@server/services/marketing/campaigns.service';
import { getSegment } from '@server/services/marketing/segments.service';
import { effectiveBranchId, resolveWritableBranchId, scopeAudienceRules, loadAccessibleOrThrow } from '@server/utils/branch-access';

export const GET = withErrorHandler(
  withAuth(async (req: AuthedRequest) => {
    const restaurantId = req.session.restaurantId!;
    await requireSection(req, 'marketing');
    await requirePlanFeature(restaurantId, 'marketing');
    // Accept both ?audienceDeleted=true and the more URL-friendly
    // ?audienceDeleted=1 so the campaigns page can power its "audience
    // deleted" filter chip without a custom encoding scheme.
    const url = new URL(req.url);
    const flag = url.searchParams.get('audienceDeleted');
    const audienceDeleted = flag === 'true' || flag === '1';
    const campaigns = await listCampaigns(restaurantId, { audienceDeleted }, effectiveBranchId(req.session));
    return NextResponse.json({ campaigns });
  })
);

export const POST = withErrorHandler(
  withAuth(
    withValidationAuthed(createCampaignSchema, async (req) => {
      const restaurantId = req.session.restaurantId!;
      await requireSection(req, 'marketing', 'create');
      await requirePlanFeature(restaurantId, 'marketing');
      const body = req.parsedBody as CreateCampaignInput;
      // Pinned staff cannot pick a different branch — they default to (and
      // are limited to) their own. Owners may target any branch in their
      // restaurant. The service then re-validates ownership against
      // restaurants table to defend against stale UI state.
      const branchId = resolveWritableBranchId(req.session, body.branchId);
      // Re-scope audienceRules.branchIds — the row-level branchId above
      // pins which branch's mailbox/template config sends the messages,
      // but the audience compiler reads rules.branchIds to decide WHICH
      // CUSTOMERS to mail. Without this guard a pinned user could craft
      // a body that sends through their branch but targets a sibling
      // branch's customer list.
      const audienceRules = body.audienceRules
        ? scopeAudienceRules(req.session, body.audienceRules)
        : undefined;
      // Pre-validate segmentId — without this, a pinned/active-branch
      // user could attach a sibling segment's UUID and inherit its rules
      // (resolveDraftRules pulls them unscoped). loadAccessibleOrThrow
      // returns 403 for sibling-branch IDs and 404 for truly missing.
      if (body.segmentId) {
        await loadAccessibleOrThrow(
          await getSegment(restaurantId, body.segmentId, effectiveBranchId(req.session)),
          () => getSegment(restaurantId, body.segmentId!, null),
          'Segment',
        );
      }
      const campaign = await createCampaign(restaurantId, {
        name: body.name,
        channel: body.channel,
        branchId,
        segmentId: body.segmentId ?? null,
        audienceRules,
        subject: body.subject ?? null,
        bodyHtml: body.bodyHtml ?? null,
        templateName: body.templateName ?? null,
        templateLang: body.templateLang ?? null,
        templateVars: body.templateVars,
        scheduledAt: body.scheduledAt ?? null,
        ignoreQuietHours: body.ignoreQuietHours,
        launchGroupId: body.launchGroupId ?? null,
        maxPerMinute: body.maxPerMinute ?? null,
        maxPerHour:   body.maxPerHour ?? null,
        createdBy: req.session.userId,
      });
      return NextResponse.json({ campaign }, { status: 201 });
    })
  )
);
