import { BadRequestException, Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
import { PrismaService } from '../common/prisma.service';
import { getMerchant } from '../common/core';
import { MarcadeoProvisioning } from '../integrations/marcadeo-provisioning.service';

const MATCH_TYPES = ['VENDOR', 'TAG', 'PRODUCT', 'ALL'];

/**
 * Campaigns / brands — run multiple brands from one store. Each order is split
 * to the right campaign (by Shopify vendor/tag/product) during order sync.
 * Kept in its own controller so the admin surface stays modular.
 */
@Controller('admin/campaigns')
export class CampaignsController {
  constructor(
    private prisma: PrismaService,
    private marcadeo: MarcadeoProvisioning,
  ) {}

  @Get()
  async list() {
    const m = await getMerchant(this.prisma);
    const rows = await this.prisma.campaign.findMany({ where: { merchantId: m.id }, orderBy: { createdAt: 'asc' } });
    return rows.map((c) => ({
      id: c.id,
      name: c.name,
      matchType: c.matchType,
      matchValue: c.matchValue,
      commissionType: c.commissionType,
      commissionValue: Number(c.commissionValue),
      active: c.active,
    }));
  }

  @Post()
  async create(@Body() body: Record<string, any>) {
    const m = await getMerchant(this.prisma);
    if (!body.name?.trim()) throw new BadRequestException('Campaign name is required');
    if (body.matchType !== 'ALL' && !body.matchValue?.trim())
      throw new BadRequestException('Enter what identifies this brand (vendor/tag/product)');
    const c = await this.prisma.campaign.create({
      data: {
        merchantId: m.id,
        name: String(body.name).trim(),
        matchType: MATCH_TYPES.includes(body.matchType) ? body.matchType : 'VENDOR',
        matchValue: String(body.matchValue || '').trim(),
        commissionType: body.commissionType === 'FLAT' ? 'FLAT' : 'PERCENT',
        commissionValue: Number(body.commissionValue) || 0,
        active: body.active !== false,
      },
    });
    // Mirror the brand into Marcadeo (advertiser + campaign + Sale goal). Fail-soft.
    await this.marcadeo.ensureCampaign(m, c);
    return { ok: true, id: c.id };
  }

  @Put(':id')
  async update(@Param('id') id: string, @Body() body: Record<string, any>) {
    const m = await getMerchant(this.prisma);
    const c = await this.prisma.campaign.findFirst({ where: { id, merchantId: m.id } });
    if (!c) throw new BadRequestException('Campaign not found');
    const data: any = {};
    if (body.name !== undefined) data.name = String(body.name).trim();
    if (body.matchType !== undefined) data.matchType = MATCH_TYPES.includes(body.matchType) ? body.matchType : 'VENDOR';
    if (body.matchValue !== undefined) data.matchValue = String(body.matchValue || '').trim();
    if (body.commissionType !== undefined) data.commissionType = body.commissionType === 'FLAT' ? 'FLAT' : 'PERCENT';
    if (body.commissionValue !== undefined) data.commissionValue = Number(body.commissionValue) || 0;
    if (body.active !== undefined) data.active = !!body.active;
    await this.prisma.campaign.update({ where: { id }, data });
    return { ok: true };
  }

  @Delete(':id')
  async remove(@Param('id') id: string) {
    await this.prisma.campaign.delete({ where: { id } });
    return { ok: true };
  }

  // ---- Goals / conversion events (a campaign can pay for more than "Sale") ----

  /** List the extra goals defined on a campaign. */
  @Get(':id/goals')
  async listGoals(@Param('id') id: string) {
    const m = await getMerchant(this.prisma);
    const c = await this.prisma.campaign.findFirst({ where: { id, merchantId: m.id } });
    if (!c) throw new BadRequestException('Campaign not found');
    const goals = await this.prisma.campaignGoal.findMany({
      where: { campaignId: id },
      orderBy: { createdAt: 'asc' },
    });
    return goals.map((g) => ({
      id: g.id,
      name: g.name,
      eventKey: g.eventKey,
      model: g.model,
      commissionType: g.commissionType,
      commissionValue: Number(g.commissionValue),
      isPrimary: g.isPrimary,
      synced: !!g.marcadeoGoalId,
    }));
  }

  /** Add a goal to a campaign, then sync it to Marcadeo. */
  @Post(':id/goals')
  async addGoal(@Param('id') id: string, @Body() body: Record<string, any>) {
    const m = await getMerchant(this.prisma);
    const c = await this.prisma.campaign.findFirst({ where: { id, merchantId: m.id } });
    if (!c) throw new BadRequestException('Campaign not found');
    if (!body.name?.trim()) throw new BadRequestException('Goal name is required');

    const eventKey = String(body.eventKey || body.name)
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/^-|-$/g, '')
      .slice(0, 30) || 'custom';
    const MODELS = ['CPS', 'CPA', 'CPL', 'CPI', 'CPM'];

    const clash = await this.prisma.campaignGoal.findFirst({ where: { campaignId: id, eventKey } });
    if (clash) throw new BadRequestException(`A goal for "${eventKey}" already exists`);

    await this.prisma.campaignGoal.create({
      data: {
        campaignId: id,
        name: String(body.name).trim(),
        eventKey,
        model: MODELS.includes(body.model) ? body.model : 'CPA',
        commissionType: body.commissionType === 'FLAT' ? 'FLAT' : 'PERCENT',
        commissionValue: Number(body.commissionValue) || 0,
        isPrimary: false,
      },
    });
    await this.marcadeo.ensureCampaignGoals(c);
    return { ok: true };
  }

  /** Remove an extra goal (app-side). */
  @Delete(':id/goals/:goalId')
  async removeGoal(@Param('id') id: string, @Param('goalId') goalId: string) {
    await this.prisma.campaignGoal.deleteMany({ where: { id: goalId, campaignId: id } });
    return { ok: true };
  }
}
