/**
 * Deals Pipeline Service
 * CRUD, stage management, stats
 */

import { prisma } from '@/lib/db';
import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';
/** Prisma-compatible type aliases for deals service */
import type { CreateDealInput, UpdateDealInput, DealFilterInput } from '@/lib/validators/deals.validator';

const log = logger.scope('DealsService');

// ═══════════════════════════════════════════════════════════════════════
// CRUD
// ═══════════════════════════════════════════════════════════════════════

export async function createDeal(userId: string, input: CreateDealInput) {
  const id = createId();
  const now = new Date();

  const deal = await prisma.deals.create({
    data: {
      id,
      userId,
      title: input.title,
      customerId: input.customerId ?? null,
      quotationId: input.quotationId ?? null,
      value: input.value ?? 0,
      currency: input.currency ?? 'QAR',
      stage: input.stage ?? 'LEAD',
      probability: input.probability ?? 0,
      expectedCloseDate: input.expectedCloseDate ? new Date(input.expectedCloseDate) : null,
      notes: input.notes ?? null,
      updatedAt: now,
    },
    include: { customer: true, quotation: true },
  });

  log.info('Deal created', { dealId: id, userId });
  return deal;
}

export async function getDeal(userId: string, dealId: string) {
  return prisma.deals.findFirst({
    where: { id: dealId, userId },
    include: { customer: true, quotation: true },
  });
}

export async function updateDeal(userId: string, dealId: string, input: UpdateDealInput) {
  const existing = await prisma.deals.findFirst({ where: { id: dealId, userId } });
  if (!existing) return null;

  const data: Record<string, unknown> = {
    ...(input.title !== undefined && { title: input.title }),
    ...(input.customerId !== undefined && { customer: input.customerId ? { connect: { id: input.customerId } } : { disconnect: true } }),
    ...(input.quotationId !== undefined && { quotation: input.quotationId ? { connect: { id: input.quotationId } } : { disconnect: true } }),
    ...(input.value !== undefined && { value: input.value }),
    ...(input.currency !== undefined && { currency: input.currency }),
    ...(input.stage !== undefined && { stage: input.stage }),
    ...(input.probability !== undefined && { probability: input.probability }),
    ...(input.expectedCloseDate !== undefined && { expectedCloseDate: input.expectedCloseDate ? new Date(input.expectedCloseDate) : null }),
    ...(input.lostReason !== undefined && { lostReason: input.lostReason }),
    ...(input.notes !== undefined && { notes: input.notes }),
    updatedAt: new Date(),
  };

  // Auto-set closedAt when moving to won/lost
  if (input.stage === 'CLOSED_WON' || input.stage === 'CLOSED_LOST') {
    (data as Record<string, unknown>).closedAt = new Date();
  }

  return prisma.deals.update({
    where: { id: dealId },
    data,
    include: { customer: true, quotation: true },
  });
}

export async function deleteDeal(userId: string, dealId: string): Promise<boolean> {
  const existing = await prisma.deals.findFirst({ where: { id: dealId, userId } });
  if (!existing) return false;

  await prisma.deals.delete({ where: { id: dealId } });
  log.info('Deal deleted', { dealId, userId });
  return true;
}

export async function listDeals(userId: string, filters: DealFilterInput) {
  const page = filters.page ?? 1;
  const limit = Math.min(filters.limit ?? 20, 100);
  const skip = (page - 1) * limit;

  const where: Record<string, unknown> = { userId };

  if (filters.stage) where.stage = filters.stage;
  if (filters.customerId) where.customerId = filters.customerId;
  if (filters.search) {
    where.OR = [
      { title: { contains: filters.search, mode: 'insensitive' } },
    ];
  }

  const [deals, total] = await Promise.all([
    prisma.deals.findMany({
      where,
      include: { customer: true, quotation: true },
      orderBy: { [filters.sortBy ?? 'createdAt']: filters.sortOrder ?? 'desc' },
      skip,
      take: limit,
    }),
    prisma.deals.count({ where }),
  ]);

  return {
    deals,
    total,
    page,
    limit,
    totalPages: Math.ceil(total / limit),
  };
}

// ═══════════════════════════════════════════════════════════════════════
// Pipeline Operations
// ═══════════════════════════════════════════════════════════════════════

export async function getDealsByStage(userId: string) {
  const stages = ['LEAD', 'QUALIFIED', 'PROPOSAL', 'NEGOTIATION', 'CLOSED_WON', 'CLOSED_LOST'] as const;

  const results = await Promise.all(
    stages.map(async (stage) => {
      const deals = await prisma.deals.findMany({
        where: { userId, stage },
        include: { customer: true },
        orderBy: { updatedAt: 'desc' },
      });
      return { stage, deals, count: deals.length, totalValue: deals.reduce((sum, d) => sum + d.value, 0) };
    }),
  );

  return results;
}

export async function moveDeal(userId: string, dealId: string, stage: string) {
  const existing = await prisma.deals.findFirst({ where: { id: dealId, userId } });
  if (!existing) return null;

  const isClosing = stage === 'CLOSED_WON' || stage === 'CLOSED_LOST';

  return prisma.deals.update({
    where: { id: dealId },
    data: {
      stage,
      ...(isClosing && { closedAt: new Date() }),
      ...(stage === 'CLOSED_WON' && { probability: 100 }),
      ...(stage === 'CLOSED_LOST' && { probability: 0 }),
      updatedAt: new Date(),
    },
    include: { customer: true, quotation: true },
  });
}

export async function getDealStats(userId: string) {
  const deals = await prisma.deals.findMany({
    where: { userId },
    select: { stage: true, value: true, probability: true, closedAt: true, createdAt: true },
  });

  const total = deals.length;
  const totalValue = deals.reduce((sum, d) => sum + d.value, 0);
  const weightedValue = deals.reduce((sum, d) => sum + d.value * (d.probability / 100), 0);
  const wonDeals = deals.filter((d) => d.stage === 'CLOSED_WON');
  const lostDeals = deals.filter((d) => d.stage === 'CLOSED_LOST');
  const openDeals = deals.filter((d) => d.stage !== 'CLOSED_WON' && d.stage !== 'CLOSED_LOST');

  const byStage: Record<string, { count: number; value: number }> = {};
  for (const deal of deals) {
    if (!byStage[deal.stage]) byStage[deal.stage] = { count: 0, value: 0 };
    byStage[deal.stage].count++;
    byStage[deal.stage].value += deal.value;
  }

  return {
    total,
    totalValue: Math.round(totalValue * 100) / 100,
    weightedValue: Math.round(weightedValue * 100) / 100,
    wonCount: wonDeals.length,
    wonValue: Math.round(wonDeals.reduce((s, d) => s + d.value, 0) * 100) / 100,
    lostCount: lostDeals.length,
    openCount: openDeals.length,
    openValue: Math.round(openDeals.reduce((s, d) => s + d.value, 0) * 100) / 100,
    winRate: wonDeals.length + lostDeals.length > 0
      ? Math.round((wonDeals.length / (wonDeals.length + lostDeals.length)) * 10000) / 100
      : 0,
    byStage,
  };
}
