import { and, eq, inArray, isNull } from 'drizzle-orm';
import { crmOpportunities, crmQuotations } from '@/db/schema';
import { auditAction } from '@/features/foundation/audit-log/service';
import { db } from '@/lib/db';

const QUOTATION_OPEN_CODES = new Set(['draft', 'revised', 'pending_approval', 'approved', 'sent_to_customer', 'sent']);
const QUOTATION_NEGOTIATION_CODES = new Set(['approved', 'sent_to_customer', 'sent']);

async function getOpportunity(opportunityId: string, organizationId: string) {
  const [opportunity] = await db
    .select()
    .from(crmOpportunities)
    .where(
      and(
        eq(crmOpportunities.id, opportunityId),
        eq(crmOpportunities.organizationId, organizationId),
        isNull(crmOpportunities.deletedAt)
      )
    )
    .limit(1);

  return opportunity ?? null;
}

async function getActiveQuotationCodes(opportunityId: string, organizationId: string) {
  const rows = await db
    .select({
      status: crmQuotations.status,
      acceptedAt: crmQuotations.acceptedAt,
      rejectedAt: crmQuotations.rejectedAt
    })
    .from(crmQuotations)
    .where(
      and(
        eq(crmQuotations.organizationId, organizationId),
        eq(crmQuotations.opportunityId, opportunityId),
        isNull(crmQuotations.deletedAt)
      )
    );

  return rows;
}

async function updateOpportunityStage(
  opportunityId: string,
  organizationId: string,
  userId: string,
  nextStage: string,
  action: string,
  quotationId: string,
  reason: string
) {
  const opportunity = await getOpportunity(opportunityId, organizationId);
  if (!opportunity || (opportunity.outcomeStatus ?? 'open') !== 'open' || opportunity.status === nextStage) {
    return;
  }

  const [updated] = await db
    .update(crmOpportunities)
    .set({
      status: nextStage,
      pipelineStage: 'opportunity',
      updatedAt: new Date(),
      updatedBy: userId
    })
    .where(eq(crmOpportunities.id, opportunityId))
    .returning();

  await auditAction({
    organizationId,
    branchId: updated.branchId,
    userId,
    entityType: 'crm_opportunity',
    entityId: opportunityId,
    action,
    afterData: {
      quotationId,
      previousOpportunityStage: opportunity.status,
      newOpportunityStage: updated.status,
      previousOutcomeStatus: opportunity.outcomeStatus ?? 'open',
      newOutcomeStatus: updated.outcomeStatus,
      reason,
      actedBy: userId
    }
  });
}

export async function syncOpportunityFromQuotationCreated(args: {
  organizationId: string;
  userId: string;
  opportunityId: string;
  quotationId: string;
}) {
  await updateOpportunityStage(
    args.opportunityId,
    args.organizationId,
    args.userId,
    'quotation_created',
    'sync_opportunity_from_quotation_created',
    args.quotationId,
    'quotation_created'
  );
}

export async function syncOpportunityFromQuotationApproval(args: {
  organizationId: string;
  userId: string;
  opportunityId: string;
  quotationId: string;
  quotationStatusCode: string | null;
}) {
  if (!args.quotationStatusCode) {
    return;
  }

  const nextStage = QUOTATION_NEGOTIATION_CODES.has(args.quotationStatusCode)
    ? 'negotiation'
    : 'quotation_created';

  const action = QUOTATION_NEGOTIATION_CODES.has(args.quotationStatusCode)
    ? args.quotationStatusCode === 'approved'
      ? 'sync_opportunity_from_quotation_approved'
      : 'sync_opportunity_from_quotation_sent'
    : 'sync_opportunity_from_quotation_created';

  await updateOpportunityStage(
    args.opportunityId,
    args.organizationId,
    args.userId,
    nextStage,
    action,
    args.quotationId,
    args.quotationStatusCode
  );
}

export async function syncOpportunityFromQuotationWon(args: {
  organizationId: string;
  userId: string;
  opportunityId: string;
  quotationId: string;
  reason?: string | null;
}) {
  const opportunity = await getOpportunity(args.opportunityId, args.organizationId);
  if (!opportunity) {
    return;
  }

  const now = new Date();
  const [updated] = await db
    .update(crmOpportunities)
    .set({
      status: 'closed',
      outcomeStatus: 'won',
      pipelineStage: 'closed_won',
      closedAt: now,
      closedWonAt: now,
      closedLostAt: null,
      closedByUserId: args.userId,
      updatedAt: now,
      updatedBy: args.userId
    })
    .where(eq(crmOpportunities.id, args.opportunityId))
    .returning();

  await auditAction({
    organizationId: args.organizationId,
    branchId: updated.branchId,
    userId: args.userId,
    entityType: 'crm_opportunity',
    entityId: args.opportunityId,
    action: 'sync_opportunity_from_quotation_won',
    afterData: {
      quotationId: args.quotationId,
      previousOpportunityStage: opportunity.status,
      newOpportunityStage: updated.status,
      previousOutcomeStatus: opportunity.outcomeStatus ?? 'open',
      newOutcomeStatus: updated.outcomeStatus,
      reason: args.reason ?? 'quotation_won',
      actedBy: args.userId
    }
  });
}

export async function syncOpportunityFromQuotationLost(args: {
  organizationId: string;
  userId: string;
  opportunityId: string;
  quotationId: string;
  reason: string;
}) {
  const opportunity = await getOpportunity(args.opportunityId, args.organizationId);
  if (!opportunity) {
    return;
  }

  const rows = await getActiveQuotationCodes(args.opportunityId, args.organizationId);
  const otherOpenExists = rows.some((row) => {
    if (row.acceptedAt || row.rejectedAt) {
      return false;
    }

    return QUOTATION_OPEN_CODES.has(row.status);
  });

  if (otherOpenExists) {
    return;
  }

  const now = new Date();
  const [updated] = await db
    .update(crmOpportunities)
    .set({
      status: 'closed',
      outcomeStatus: 'lost',
      pipelineStage: 'closed_lost',
      closedAt: now,
      closedWonAt: null,
      closedLostAt: now,
      closedByUserId: args.userId,
      updatedAt: now,
      updatedBy: args.userId
    })
    .where(eq(crmOpportunities.id, args.opportunityId))
    .returning();

  await auditAction({
    organizationId: args.organizationId,
    branchId: updated.branchId,
    userId: args.userId,
    entityType: 'crm_opportunity',
    entityId: args.opportunityId,
    action: 'sync_opportunity_from_quotation_lost',
    afterData: {
      quotationId: args.quotationId,
      previousOpportunityStage: opportunity.status,
      newOpportunityStage: updated.status,
      previousOutcomeStatus: opportunity.outcomeStatus ?? 'open',
      newOutcomeStatus: updated.outcomeStatus,
      reason: args.reason,
      actedBy: args.userId
    }
  });
}
