import * as dealQueries from '@/server/db/queries/deals.js';
import * as groupDealQueries from '@/server/db/queries/group-deals.js';
import { applyEffects } from './apply-effects.js';
import { transition } from './machine.js';
import {
  buildCtx,
  GroupDealError,
  makeApplyCtx,
  safeArmPartialDecisionAlarm,
  type GroupDealDeps,
  withoutAlarmEffects,
} from './runtime.js';
import { executeGroupDeal } from './execute-handler.js';
import { captureCaught } from '@/server/observability/capture.server.js';

export async function handlePartialExecution(deps: GroupDealDeps, groupDealId: string) {
  const groupDeal = await groupDealQueries.findById(deps.db, groupDealId);
  if (!groupDeal) throw new GroupDealError('GROUP_DEAL_NOT_FOUND', 'Group deal not found');

  const deal = await dealQueries.findById(deps.db, groupDeal.dealId);
  if (!deal) throw new GroupDealError('DEAL_NOT_FOUND', 'Parent deal not found');

  const partialDecisionDeadline = new Date(Date.now() + 24 * 60 * 60 * 1000);
  const partialResult = transition(buildCtx(groupDeal, deal), {
    kind: 'window_expired_below_threshold',
    at: partialDecisionDeadline,
  });
  if (!partialResult.ok) {
    throw new GroupDealError(
      'INVALID_STATE',
      `Cannot move to PARTIAL_PENDING from ${groupDeal.groupState}`,
    );
  }

  await groupDealQueries.updateGroupState(deps.db, groupDealId, partialResult.nextState, {
    partialDecisionDeadline,
  });

  const partialEffects = withoutAlarmEffects(
    partialResult.effects.map((effect) =>
      effect.kind === 'enqueue-outbox'
        ? {
            ...effect,
            payload: {
              ...effect.payload,
              currentCount: groupDeal.currentReservationCount,
              minGroupSize: groupDeal.minGroupSize,
              partialDecisionDeadline: partialDecisionDeadline.toISOString(),
            },
          }
        : effect,
    ),
  );
  await safeArmPartialDecisionAlarm(deps.doClient, groupDealId, partialDecisionDeadline);
  await applyEffects(
    makeApplyCtx(deps, (id) => executeGroupDeal(deps, id)),
    partialEffects,
  );

  try {
    await deps.push.sendToVendor(deal.vendorId, {
      title: 'עסקת קבוצה - החלטה נדרשת',
      body: `${deal.title} - ${groupDeal.currentReservationCount}/${groupDeal.minGroupSize} משתתפים. יש לך 24 שעות להחליט.`,
      url: `/vendor/deals/${deal.id}`,
      tag: 'group_deal_partial',
      data: { groupDealId, dealTitle: deal.title },
    });
  } catch (err) {
    captureCaught(err, { scope: 'server.workflows.group-deal', severity: 'warning' });
  }
}

export async function honorPartialGroupDeal(deps: GroupDealDeps, groupDealId: string) {
  const groupDeal = await groupDealQueries.findById(deps.db, groupDealId);
  if (!groupDeal) throw new GroupDealError('GROUP_DEAL_NOT_FOUND', 'Group deal not found');

  const honorResult = transition(buildCtx(groupDeal, null), {
    kind: 'vendor_honor_partial',
    at: new Date(),
  });
  if (!honorResult.ok) {
    throw new GroupDealError(
      'INVALID_STATE',
      `Cannot honor a group deal in state ${groupDeal.groupState}. Expected PARTIAL_PENDING.`,
    );
  }

  await groupDealQueries.updateGroupState(deps.db, groupDealId, 'VENDOR_HONORED', {
    vendorHonoredAt: new Date(),
  });

  return executeGroupDeal(deps, groupDealId);
}
