import { and, count, eq } from 'drizzle-orm';
import * as vendorQueries from '@/server/db/queries/vendors.js';
import * as groupTierQueries from '@/server/db/queries/group-tiers.js';
import { deals, dealSkus, groupDeals, type groupTiers } from '@/server/db/schema.js';
import { firstZodIssue } from '@/lib/validation/zod.js';
import { createGroupDealBodySchema } from '@/server/schemas/group-deal.js';
import type { CreateGroupDealBody } from '@/server/schemas/group-deal.js';
import { getSystemConfigCached } from '@/server/db/queries/system-config.js';
import { enqueueLlmJob } from '@/server/ai/llm.js';
import { sendLlmJobToQueue } from '@/server/queues/llm-jobs-producer.js';
import { buildDefaultSkuRow } from '@/server/domain/variants/default-sku.js';
import { refreshDealCache } from '@/server/domain/variants/cache.js';
import { setInventory, toInventoryTx } from '@/server/stock/inventory-platform.js';
import {
  DiscountTooLowError,
  DraftCapExceededError,
  DuplicateContentError,
  computeContentHash,
} from '@/server/workflows/vendor-deal.js';
import type { TierInput } from '@/server/db/queries/group-tiers.js';
import type { FillRule } from '@/lib/enums/fill-rule.js';
import type { CancellationPolicy } from '@/lib/enums/cancellation-policy.js';
import { env } from '@/server/env.js';
import { captureCaught } from '@/server/observability/capture.server.js';
import { invalidateCatalog } from '@/server/cache/invalidate.js';
import { GroupDealError, type GroupDealDeps } from './runtime.js';

type GroupDealRow = typeof groupDeals.$inferSelect;
type DealRow = { id: string; vendorId: string; title: string; windowEnd: Date | null };
type TierRow = typeof groupTiers.$inferSelect;

async function persistGroupDeal(
  deps: GroupDealDeps,
  deal: DealRow,
  isVeteran: boolean,
  data: {
    fillRule: FillRule;
    minGroupSize: number;
    maxGroupSize: number;
    perCustomerLimit: number | null;
    cancellationPolicy: CancellationPolicy;
    tieredPricingEnabled: boolean;
    earlyBirdEnabled: boolean;
    earlyBirdSlots: number | null;
    earlyBirdDiscountPercent: number | null;
    tiers?: TierInput[];
  },
): Promise<{ groupDeal: GroupDealRow; tiers: TierRow[] }> {
  const [groupDeal] = await deps.db
    .insert(groupDeals)
    .values({
      dealId: deal.id,
      fillRule: data.fillRule,
      groupState: 'COLLECTING',
      minGroupSize: data.minGroupSize,
      maxGroupSize: data.maxGroupSize,
      perCustomerLimit: data.perCustomerLimit ?? 1,
      cancellationPolicy: data.cancellationPolicy,
      tieredPricingEnabled: data.tieredPricingEnabled,
      earlyBirdEnabled: data.earlyBirdEnabled,
      earlyBirdSlots: data.earlyBirdSlots,
      earlyBirdDiscountPercent: data.earlyBirdDiscountPercent,
    })
    .returning();
  if (!groupDeal) throw new GroupDealError('DB_ERROR', 'Failed to create group_deal row');

  if (isVeteran && deal.windowEnd) {
    try {
      await deps.doClient.armDealAlarm(deal.id, deal.windowEnd);
    } catch (err) {
      captureCaught(err, { scope: 'armDealAlarm', extra: { dealId: deal.id } });
    }
    try {
      await deps.doClient.armGroupDealAlarm(groupDeal.id, deal.windowEnd, 'deadline');
    } catch (err) {
      captureCaught(err, { scope: 'armGroupDealAlarm', extra: { groupDealId: groupDeal.id } });
    }
  }

  let tiers: TierRow[] = [];
  if (data.tieredPricingEnabled && data.tiers && data.tiers.length > 0) {
    tiers = await groupTierQueries.createMany(deps.db, groupDeal.id, data.tiers);
  }

  return { groupDeal, tiers };
}

async function dispatchGroupDealOutbox(
  deps: Pick<GroupDealDeps, 'db'>,
  deal: DealRow,
  vendorId: string,
  data: {
    description: string;
    category?: string | null;
    originalPrice: number;
    discountedPrice: number;
    discountPercent: number;
  },
): Promise<void> {
  const jobId = await enqueueLlmJob(deps.db, {
    jobType: 'DEAL_MODERATION',
    targetId: deal.id,
    targetType: 'DEAL',
    inputPayload: {
      dealId: deal.id,
      vendorId,
      title: deal.title,
      description: data.description,
      category: data.category ?? null,
      dealType: 'GROUP',
      originalPrice: data.originalPrice,
      discountedPrice: data.discountedPrice,
      discountPercent: data.discountPercent,
      primaryImageKey: null,
    },
  });
  await sendLlmJobToQueue(env, jobId);
}

export async function createGroupDeal(
  deps: GroupDealDeps,
  vendorId: string,
  input: CreateGroupDealBody,
) {
  const parsed = createGroupDealBodySchema.safeParse(input);
  if (!parsed.success) {
    throw new GroupDealError('VALIDATION_ERROR', firstZodIssue(parsed.error));
  }

  const vendor = await vendorQueries.findById(deps.db, vendorId);
  if (!vendor) throw new GroupDealError('VENDOR_NOT_FOUND', 'Vendor not found');

  const publishableStates = ['ACTIVE', 'VETERAN'] as const;
  const vendorCanPublish = (publishableStates as readonly string[]).includes(vendor.accountState);

  if (!vendorCanPublish) {
    const [draftRow] = await deps.db
      .select({ n: count() })
      .from(deals)
      .where(and(eq(deals.vendorId, vendorId), eq(deals.dealState, 'DRAFT')));
    const draftCount = draftRow?.n ?? 0;
    if (draftCount >= 3) {
      throw new DraftCapExceededError(3);
    }

    const deal = await deps.db.transaction(async (tx) => {
      const [createdDeal] = await tx
        .insert(deals)
        .values({
          vendorId,
          dealType: 'GROUP',
          title: parsed.data.title,
          description: parsed.data.description ?? '',
          windowEnd: new Date(parsed.data.deadline),
          pickupAddress: parsed.data.pickupAddress ?? '',
          specialInstructions: parsed.data.specialInstructions,
          dealState: 'DRAFT',
        })
        .returning();
      if (!createdDeal) throw new GroupDealError('DB_ERROR', 'Failed to create deal row');

      const [groupSku] = await tx
        .insert(dealSkus)
        .values(
          buildDefaultSkuRow({
            dealId: createdDeal.id,
            originalPrice: parsed.data.originalPrice,
            discountPercent: parsed.data.discountPercent,
            discountedPrice: parsed.data.discountedPrice,
            quantityTotal: parsed.data.maxGroupSize,
          }),
        )
        .returning({ id: dealSkus.id });
      if (groupSku) {
        await setInventory(toInventoryTx(tx), {
          skuId: groupSku.id,
          vendorId: createdDeal.vendorId,
          quantityTotal: parsed.data.maxGroupSize,
        });
      }
      await refreshDealCache(tx, createdDeal.id);
      return createdDeal;
    });

    const [groupDeal] = await deps.db
      .insert(groupDeals)
      .values({
        dealId: deal.id,
        fillRule: parsed.data.fillRule,
        groupState: 'COLLECTING',
        minGroupSize: parsed.data.minGroupSize,
        maxGroupSize: parsed.data.maxGroupSize,
        perCustomerLimit: parsed.data.perCustomerLimit,
        cancellationPolicy: parsed.data.cancellationPolicy,
        tieredPricingEnabled: parsed.data.tieredPricingEnabled,
        earlyBirdEnabled: parsed.data.earlyBirdEnabled,
        earlyBirdSlots: parsed.data.earlyBirdSlots,
        earlyBirdDiscountPercent: parsed.data.earlyBirdDiscountPercent,
      })
      .returning();
    if (!groupDeal) throw new GroupDealError('DB_ERROR', 'Failed to create group_deal row');

    let tiers: TierRow[] = [];
    if (parsed.data.tieredPricingEnabled && parsed.data.tiers && parsed.data.tiers.length > 0) {
      tiers = await groupTierQueries.createMany(deps.db, groupDeal.id, parsed.data.tiers);
    }

    return { deal, groupDeal, tiers };
  }

  const minDiscountStr = await getSystemConfigCached(deps.db, 'deal_min_discount_percent');
  const minDiscount = parseInt(minDiscountStr, 10) || 0;
  if (parsed.data.discountPercent < minDiscount) {
    throw new DiscountTooLowError(minDiscount);
  }

  const contentHash = await computeContentHash(
    vendorId,
    parsed.data.title,
    parsed.data.description ?? '',
    parsed.data.discountPercent,
  );
  const existing = await deps.db
    .select({ id: deals.id, dealState: deals.dealState, createdAt: deals.createdAt })
    .from(deals)
    .where(and(eq(deals.vendorId, vendorId), eq(deals.contentHash, contentHash)));
  if (existing.length > 0) {
    const inFlight = existing.filter((row) =>
      ['UNDER_REVIEW', 'PENDING_APPROVAL', 'ACTIVE', 'PAUSED'].includes(row.dealState),
    );
    if (inFlight.length > 0) {
      throw new DuplicateContentError(
        'An identical deal is already active or in the approval pipeline.',
      );
    }
    const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
    const recentlyRejected = existing.filter(
      (row) =>
        row.dealState === 'REJECTED' && row.createdAt != null && new Date(row.createdAt) > sevenDaysAgo,
    );
    if (recentlyRejected.length > 0) {
      throw new DuplicateContentError(
        'This deal was rejected recently. Please wait 7 days before resubmitting identical content.',
      );
    }
  }

  const isVeteran = vendor.tier === 'VETERAN';
  const initialDealState = isVeteran ? 'ACTIVE' : 'UNDER_REVIEW';
  const deadline = new Date(parsed.data.deadline);

  const deal = await deps.db.transaction(async (tx) => {
    const [createdDeal] = await tx
      .insert(deals)
      .values({
        vendorId,
        dealType: 'GROUP',
        title: parsed.data.title,
        description: parsed.data.description ?? '',
        windowEnd: deadline,
        pickupAddress: parsed.data.pickupAddress ?? '',
        specialInstructions: parsed.data.specialInstructions,
        dealState: initialDealState,
        contentHash,
        ...(isVeteran ? { approvedAt: new Date(), approvedBy: 'AI_AGENT' as const } : {}),
      })
      .returning();
    if (!createdDeal) throw new GroupDealError('DB_ERROR', 'Failed to create deal row');

    const [groupSku] = await tx
      .insert(dealSkus)
      .values(
        buildDefaultSkuRow({
          dealId: createdDeal.id,
          originalPrice: parsed.data.originalPrice,
          discountPercent: parsed.data.discountPercent,
          discountedPrice: parsed.data.discountedPrice,
          quantityTotal: parsed.data.maxGroupSize,
        }),
      )
      .returning({ id: dealSkus.id });
    if (groupSku) {
      await setInventory(toInventoryTx(tx), {
        skuId: groupSku.id,
        vendorId: createdDeal.vendorId,
        quantityTotal: parsed.data.maxGroupSize,
      });
    }
    await refreshDealCache(tx, createdDeal.id);
    return createdDeal;
  });

  const { groupDeal, tiers } = await persistGroupDeal(deps, deal, isVeteran, {
    fillRule: parsed.data.fillRule,
    minGroupSize: parsed.data.minGroupSize,
    maxGroupSize: parsed.data.maxGroupSize,
    perCustomerLimit: parsed.data.perCustomerLimit,
    cancellationPolicy: parsed.data.cancellationPolicy,
    tieredPricingEnabled: parsed.data.tieredPricingEnabled,
    earlyBirdEnabled: parsed.data.earlyBirdEnabled,
    earlyBirdSlots: parsed.data.earlyBirdSlots ?? null,
    earlyBirdDiscountPercent: parsed.data.earlyBirdDiscountPercent ?? null,
    tiers: parsed.data.tiers,
  });

  if (!isVeteran) {
    await dispatchGroupDealOutbox({ db: deps.db }, deal, vendorId, {
      description: parsed.data.description ?? '',
      category: parsed.data.category,
      originalPrice: parseFloat(parsed.data.originalPrice),
      discountedPrice: parseFloat(parsed.data.discountedPrice),
      discountPercent: parsed.data.discountPercent,
    });
  } else {
    await invalidateCatalog(deps.db, { scope: 'deal', dealId: deal.id });
  }

  return { deal, groupDeal, tiers };
}
