/**
 * Lead Nurture Service
 * Multi-channel nurture sequences with enrollment, step execution, and cron processing
 */

import {
  leadsAdapter,
  leadNurtureSequencesAdapter,
  leadNurtureStepsAdapter,
  leadNurtureMembershipsAdapter,
} from '@/lib/adapters/leads';
import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';
import type {
  NurtureSequenceInput,
  NurtureSequenceOutput,
  NurtureStepOutput,
  NurtureMembershipOutput,
  LeadNurtureTrigger,
  LeadNurtureChannel,
  LeadNurtureMembershipStatus,
} from '@/lib/types/lead-crm.types';
/** JSON-compatible value (replaces JsonValue) */
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
/** JSON-compatible input value (replaces InputJsonValue) */
type InputJsonValue = string | number | boolean | null | InputJsonValue[] | { [key: string]: InputJsonValue };
/** Represents JSON null (replaces JsonNull) */
const JsonNull = null;

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

// ═══════════════════════════════════════════════════════════════════════
// Sequence CRUD
// ═══════════════════════════════════════════════════════════════════════

export async function getSequences(userId: string): Promise<NurtureSequenceOutput[]> {
  const sequences = await leadNurtureSequencesAdapter.findMany({
    where: { userId },
    include: { steps: { orderBy: { stepOrder: 'asc' } } },
    orderBy: { createdAt: 'desc' },
  });
  return sequences.map(mapSequenceToOutput);
}

export async function createSequence(userId: string, input: NurtureSequenceInput): Promise<NurtureSequenceOutput> {
  const sequence = await leadNurtureSequencesAdapter.create({
    data: {
      id: createId(),
      userId,
      name: input.name,
      description: input.description ?? null,
      triggerType: input.triggerType as LeadNurtureTrigger,
      triggerConditions: input.triggerConditions ? (input.triggerConditions as InputJsonValue) : JsonNull,
      steps: {
        create: input.steps.map((step) => ({
          id: createId(),
          stepOrder: step.stepOrder,
          channel: step.channel as LeadNurtureChannel,
          delayHours: step.delayHours,
          subject: step.subject ?? null,
          content: step.content ?? null,
          config: step.config ? (step.config as InputJsonValue) : JsonNull,
        })),
      },
    },
    include: { steps: { orderBy: { stepOrder: 'asc' } } },
  });

  log.info('Nurture sequence created', { sequenceId: sequence.id, userId });
  return mapSequenceToOutput(sequence);
}

export async function updateSequence(
  userId: string,
  seqId: string,
  input: {
    name?: string;
    description?: string;
    triggerType?: LeadNurtureTrigger;
    triggerConditions?: Record<string, unknown>;
    isActive?: boolean;
    steps?: NurtureSequenceInput['steps'];
  },
): Promise<NurtureSequenceOutput | null> {
  const existing = await leadNurtureSequencesAdapter.findFirst({ where: { id: seqId, userId } });
  if (!existing) return null;

  // If steps are being replaced, delete old and create new
  if (input.steps) {
    await leadNurtureStepsAdapter.deleteMany({ where: { sequenceId: seqId } });
    await Promise.all(
      input.steps.map((step) =>
        leadNurtureStepsAdapter.create({
          data: {
            id: createId(),
            sequenceId: seqId,
            stepOrder: step.stepOrder,
            channel: step.channel as LeadNurtureChannel,
            delayHours: step.delayHours,
            subject: step.subject ?? null,
            content: step.content ?? null,
            config: step.config ? (step.config as InputJsonValue) : JsonNull,
          },
        }),
      ),
    );
  }

  const sequence = await leadNurtureSequencesAdapter.update({
    where: { id: seqId },
    data: {
      ...(input.name !== undefined && { name: input.name }),
      ...(input.description !== undefined && { description: input.description }),
      ...(input.triggerType !== undefined && { triggerType: input.triggerType }),
      ...(input.triggerConditions !== undefined && { triggerConditions: input.triggerConditions as InputJsonValue }),
      ...(input.isActive !== undefined && { isActive: input.isActive }),
    },
    include: { steps: { orderBy: { stepOrder: 'asc' } } },
  });

  return mapSequenceToOutput(sequence);
}

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

  await leadNurtureSequencesAdapter.delete({ where: { id: seqId } });
  return true;
}

// ═══════════════════════════════════════════════════════════════════════
// Enrollment
// ═══════════════════════════════════════════════════════════════════════

export async function enrollLead(userId: string, leadId: string, sequenceId: string): Promise<NurtureMembershipOutput> {
  // Verify ownership
  const lead = await leadsAdapter.findFirst({ where: { id: leadId, userId } });
  if (!lead) throw new Error('Lead not found');

  const sequence = await leadNurtureSequencesAdapter.findFirst({
    where: { id: sequenceId, userId },
    include: { steps: { orderBy: { stepOrder: 'asc' }, take: 1 } },
  });
  if (!sequence) throw new Error('Sequence not found');

  // Check if already enrolled
  const existingMembership = await leadNurtureMembershipsAdapter.findUnique({
    where: { leadId_sequenceId: { leadId, sequenceId } },
  });
  if (existingMembership && existingMembership.status === 'ACTIVE') {
    throw new Error('Lead is already enrolled in this sequence');
  }

  const firstStep = (sequence as unknown as { steps: Array<{ delayHours: number }> }).steps[0];
  const nextActionAt = firstStep
    ? new Date(Date.now() + firstStep.delayHours * 60 * 60 * 1000)
    : null;

  const membership = await leadNurtureMembershipsAdapter.upsert({
    where: { leadId_sequenceId: { leadId, sequenceId } },
    update: {
      status: 'ACTIVE',
      currentStep: 0,
      nextActionAt,
      completedAt: null,
      exitedAt: null,
      exitReason: null,
    },
    create: {
      id: createId(),
      leadId,
      sequenceId,
      currentStep: 0,
      status: 'ACTIVE',
      nextActionAt,
    },
  });

  // Increment enrolled count
  await leadNurtureSequencesAdapter.update({
    where: { id: sequenceId },
    data: { enrolledCount: { increment: 1 } },
  });

  log.info('Lead enrolled in nurture sequence', { leadId, sequenceId });
  return mapMembershipToOutput(membership);
}

export async function exitLead(userId: string, leadId: string, sequenceId: string, reason?: string): Promise<NurtureMembershipOutput | null> {
  const lead = await leadsAdapter.findFirst({ where: { id: leadId, userId } });
  if (!lead) return null;

  const membership = await leadNurtureMembershipsAdapter.findUnique({
    where: { leadId_sequenceId: { leadId, sequenceId } },
  });
  if (!membership) return null;

  const updated = await leadNurtureMembershipsAdapter.update({
    where: { id: membership.id },
    data: {
      status: 'EXITED',
      exitedAt: new Date(),
      exitReason: reason ?? 'Manual exit',
      nextActionAt: null,
    },
  });

  log.info('Lead exited nurture sequence', { leadId, sequenceId, reason });
  return mapMembershipToOutput(updated);
}

// ═══════════════════════════════════════════════════════════════════════
// Cron Processing
// ═══════════════════════════════════════════════════════════════════════

export async function processScheduledSteps(): Promise<{ processed: number; succeeded: number; failed: number }> {
  const now = new Date();
  let processed = 0;
  let succeeded = 0;
  let failed = 0;

  const dueMemberships = await leadNurtureMembershipsAdapter.findMany({
    where: {
      status: 'ACTIVE',
      nextActionAt: { lte: now },
    },
    include: {
      sequence: { include: { steps: { orderBy: { stepOrder: 'asc' } } } },
      lead: true,
    },
    take: 100,
  });

  for (const membership of dueMemberships) {
    processed++;
    try {
      const m = membership as unknown as {
        id: string;
        sequenceId: string;
        currentStep: number;
        sequence: { steps: Array<{ channel: string; subject: string | null; content: string | null; config: JsonValue; delayHours: number }> };
        lead: { id: string; name: string; email: string | null; phone: string | null };
      };
      const steps = m.sequence.steps;
      const currentStep = steps[m.currentStep];

      if (!currentStep) {
        // Sequence completed
        await leadNurtureMembershipsAdapter.update({
          where: { id: m.id },
          data: { status: 'COMPLETED', completedAt: now, nextActionAt: null },
        });
        await leadNurtureSequencesAdapter.update({
          where: { id: m.sequenceId },
          data: { completedCount: { increment: 1 } },
        });
        succeeded++;
        continue;
      }

      // Execute the step
      await executeStep(currentStep, m.lead);

      // Advance to next step
      const nextStepIndex = m.currentStep + 1;
      const nextStep = steps[nextStepIndex];

      if (nextStep) {
        const nextActionAt = new Date(Date.now() + nextStep.delayHours * 60 * 60 * 1000);
        await leadNurtureMembershipsAdapter.update({
          where: { id: m.id },
          data: { currentStep: nextStepIndex, nextActionAt },
        });
      } else {
        // No more steps - complete
        await leadNurtureMembershipsAdapter.update({
          where: { id: m.id },
          data: { status: 'COMPLETED', completedAt: now, nextActionAt: null, currentStep: nextStepIndex },
        });
        await leadNurtureSequencesAdapter.update({
          where: { id: m.sequenceId },
          data: { completedCount: { increment: 1 } },
        });
      }

      succeeded++;
    } catch (error) {
      failed++;
      log.error('Nurture step execution failed', error, { membershipId: (membership as Record<string, unknown>).id });
    }
  }

  log.info('Nurture cron completed', { processed, succeeded, failed });
  return { processed, succeeded, failed };
}

async function executeStep(
  step: { channel: string; subject: string | null; content: string | null; config: JsonValue },
  lead: { id: string; name: string; email: string | null; phone: string | null },
): Promise<void> {
  switch (step.channel) {
    case 'EMAIL':
      // TODO: integrate with email service when ready
      log.info('Nurture email step (placeholder)', { leadId: lead.id, subject: step.subject });
      break;

    case 'WHATSAPP':
      // TODO: integrate with WhatsApp webhook when ready
      log.info('Nurture WhatsApp step (placeholder)', { leadId: lead.id, phone: lead.phone });
      break;

    case 'SMS':
      log.info('Nurture SMS step (placeholder)', { leadId: lead.id, phone: lead.phone });
      break;

    case 'WAIT':
      // WAIT steps just advance the nextActionAt, no action needed
      break;

    case 'CONDITION':
      // Evaluate condition from config - for now just pass through
      log.info('Nurture condition step (placeholder)', { leadId: lead.id });
      break;

    default:
      log.warn('Unknown nurture channel', { channel: step.channel, leadId: lead.id });
  }
}

// ═══════════════════════════════════════════════════════════════════════
// Mappers
// ═══════════════════════════════════════════════════════════════════════

function mapSequenceToOutput(seq: Record<string, unknown>): NurtureSequenceOutput {
  const s = seq as Record<string, unknown>;
  return {
    id: s.id as string,
    userId: s.userId as string,
    name: s.name as string,
    description: (s.description as string) ?? null,
    triggerType: s.triggerType as LeadNurtureTrigger,
    triggerConditions: (s.triggerConditions as Record<string, unknown>) ?? null,
    isActive: s.isActive as boolean,
    enrolledCount: s.enrolledCount as number,
    completedCount: s.completedCount as number,
    steps: s.steps ? (s.steps as Record<string, unknown>[]).map(mapStepToOutput) : [],
    createdAt: s.createdAt as Date,
    updatedAt: s.updatedAt as Date,
  };
}

function mapStepToOutput(step: Record<string, unknown>): NurtureStepOutput {
  const st = step as Record<string, unknown>;
  return {
    id: st.id as string,
    sequenceId: st.sequenceId as string,
    stepOrder: st.stepOrder as number,
    channel: st.channel as LeadNurtureChannel,
    delayHours: st.delayHours as number,
    subject: (st.subject as string) ?? null,
    content: (st.content as string) ?? null,
    config: (st.config as Record<string, unknown>) ?? null,
    createdAt: st.createdAt as Date,
    updatedAt: st.updatedAt as Date,
  };
}

function mapMembershipToOutput(m: Record<string, unknown>): NurtureMembershipOutput {
  const mb = m as Record<string, unknown>;
  return {
    id: mb.id as string,
    leadId: mb.leadId as string,
    sequenceId: mb.sequenceId as string,
    currentStep: mb.currentStep as number,
    status: mb.status as LeadNurtureMembershipStatus,
    nextActionAt: (mb.nextActionAt as Date) ?? null,
    completedAt: (mb.completedAt as Date) ?? null,
    exitedAt: (mb.exitedAt as Date) ?? null,
    exitReason: (mb.exitReason as string) ?? null,
    createdAt: mb.createdAt as Date,
    updatedAt: mb.updatedAt as Date,
  };
}
