import { eq, and } from 'drizzle-orm';
import { db } from '../db/index';
import { approvals, tasks, pipelines } from '../db/schema';
import { eventBus } from '../events/bus';
import { completeTask } from './task.service';

// ═══════════════════════════════════════════════════════════
// Approval Service — Gate management
// ═══════════════════════════════════════════════════════════

export async function approveGate(
  approvalId: string,
  userId: string,
  opts?: { notes?: string; conditions?: string[] }
) {
  const approval = await db.query.approvals.findFirst({
    where: eq(approvals.id, approvalId),
    with: { task: true, pipeline: true },
  });

  if (!approval) throw new Error('Approval not found');
  if (approval.status !== 'pending') throw new Error('Approval is not pending');

  const now = new Date();
  const newApproverCount = (approval.currentApprovers || 0) + 1;
  const isFullyApproved = newApproverCount >= (approval.requiredApprovers || 1);

  // Update approval record
  const [updated] = await db
    .update(approvals)
    .set({
      status: isFullyApproved ? 'approved' : 'pending',
      approvedBy: userId,
      approvedAt: now,
      currentApprovers: newApproverCount,
      conditions: opts?.conditions || [],
    })
    .where(eq(approvals.id, approvalId))
    .returning();

  // If fully approved, complete the linked task
  if (isFullyApproved && approval.taskId) {
    await completeTask(approval.taskId, 'approved', userId, {
      notes: opts?.notes,
      data: { approvalId, conditions: opts?.conditions },
    });
  }

  // Emit event
  await eventBus.emit({
    type: 'approval.approved',
    timestamp: now.toISOString(),
    data: {
      approvalId,
      taskId: approval.taskId,
      pipelineId: approval.pipelineId,
      stageName: approval.stageName,
      status: 'approved',
      resolvedBy: userId,
      notes: opts?.notes,
    },
    actor: { type: 'user', id: userId },
  });

  return updated;
}

export async function rejectGate(
  approvalId: string,
  userId: string,
  opts: { reason: string; requestedChanges?: string[]; severity?: string }
) {
  const approval = await db.query.approvals.findFirst({
    where: eq(approvals.id, approvalId),
    with: { task: true },
  });

  if (!approval) throw new Error('Approval not found');
  if (approval.status !== 'pending') throw new Error('Approval is not pending');

  const now = new Date();

  // Update approval record
  const [updated] = await db
    .update(approvals)
    .set({
      status: 'rejected',
      approvedBy: userId,
      approvedAt: now,
      rejectionReason: opts.reason,
    })
    .where(eq(approvals.id, approvalId))
    .returning();

  // Complete the linked task as rejected
  if (approval.taskId) {
    await completeTask(approval.taskId, 'rejected', userId, {
      notes: opts.reason,
      data: {
        approvalId,
        requestedChanges: opts.requestedChanges,
        severity: opts.severity,
      },
    });
  }

  // Emit event
  await eventBus.emit({
    type: 'approval.rejected',
    timestamp: now.toISOString(),
    data: {
      approvalId,
      taskId: approval.taskId,
      pipelineId: approval.pipelineId,
      stageName: approval.stageName,
      status: 'rejected',
      resolvedBy: userId,
      reason: opts.reason,
    },
    actor: { type: 'user', id: userId },
  });

  return updated;
}

export async function escalateGate(
  approvalId: string,
  userId: string,
  opts: { escalateTo?: string; reason?: string }
) {
  const approval = await db.query.approvals.findFirst({
    where: eq(approvals.id, approvalId),
    with: { task: true },
  });

  if (!approval) throw new Error('Approval not found');
  if (approval.status !== 'pending') throw new Error('Approval is not pending');

  const now = new Date();

  // Update approval
  const [updated] = await db
    .update(approvals)
    .set({ status: 'escalated' })
    .where(eq(approvals.id, approvalId))
    .returning();

  // Update linked task
  if (approval.taskId) {
    await db
      .update(tasks)
      .set({
        status: 'escalated',
        escalationLevel: (approval.task?.escalationLevel || 0) + 1,
        assigneeId: opts.escalateTo || undefined,
        updatedAt: now,
      })
      .where(eq(tasks.id, approval.taskId));
  }

  // Emit event
  await eventBus.emit({
    type: 'approval.escalated',
    timestamp: now.toISOString(),
    data: {
      approvalId,
      taskId: approval.taskId,
      pipelineId: approval.pipelineId,
      escalateTo: opts.escalateTo,
      reason: opts.reason,
    },
    actor: { type: 'user', id: userId },
  });

  return updated;
}

export async function listApprovals(filters: {
  status?: string;
  pipelineId?: string;
}) {
  const conditions = [];
  if (filters.status) conditions.push(eq(approvals.status, filters.status));
  if (filters.pipelineId) conditions.push(eq(approvals.pipelineId, filters.pipelineId));

  const where = conditions.length > 0 ? and(...conditions) : undefined;

  return db.query.approvals.findMany({
    where,
    with: { task: true, pipeline: true },
    orderBy: (approvals, { desc }) => [desc(approvals.createdAt)],
  });
}

export async function getApproval(id: string) {
  return db.query.approvals.findFirst({
    where: eq(approvals.id, id),
    with: { task: true, pipeline: true },
  });
}
