// src/server/features/blacklist/lib/blacklist.workflow.ts
// Approval workflow logic for blacklist entries

import { db } from '../../../shared/db/client';
import { blacklist, blacklistPending } from '../db/blacklist.table';
import { approvalRequests } from '../../approval/db/approval_requests.table';
import { employees } from '../../../shared/db/schema';
import { eq, and, or } from 'drizzle-orm';
import { randomUUID } from 'node:crypto';
import { logger } from '../../../shared/lib/logger';
import { mapToBlacklistEntry, mapToBlacklistPendingEntry } from './blacklist.mapper';
import { notifyBlacklistUpdate } from './blacklist.notifications';
import type { BlacklistPendingEntry } from '../blacklist.schema';

/**
 * Согласовать отдельную запись (Admin only)
 */
export async function approveBlacklistEntry(pendingId: string): Promise<void> {
  // Находим pending запись - ищем по id или targetId
  const pendingEntries = await db.select()
    .from(blacklistPending)
    .where(or(
      eq(blacklistPending.id, pendingId),
      eq(blacklistPending.targetId, pendingId)
    ))
    .limit(1);

  if (!pendingEntries[0]) {
    throw new Error('Запись на согласование не найдена');
  }

  const entry = pendingEntries[0];
  const isDeleteRequest = entry.status === 'to_delete';

  // Используем транзакцию для атомарности
  await db.transaction(async (tx) => {
    let finalId: string | null = null;

    if (isDeleteRequest) {
      // Если есть targetId, физически удаляем оригинальную запись
      if (entry.targetId) {
        await tx.delete(blacklist)
          .where(eq(blacklist.id, entry.targetId));

        logger.info(`[Blacklist] Delete approved (hard delete) for entry: ${entry.name}`);
      }
    } else {
      // Если есть targetId, обновляем оригинальную запись
      if (entry.targetId) {
        await tx.update(blacklist)
          .set({
            name: entry.name,
            phone: entry.phone,
            bookingEngineCheck: entry.bookingEngineCheck,
            comment: entry.comment,
            isApproved: true,
            updatedAt: new Date(),
          })
          .where(eq(blacklist.id, entry.targetId));

        finalId = entry.targetId;
        logger.info(`[Blacklist] Update approved for entry: ${entry.name}`);
      } else {
        // Если нет targetId, создаем новую запись в blacklist
        const result = await tx.insert(blacklist)
          .values({
            id: randomUUID(),
            name: entry.name,
            phone: entry.phone,
            bookingEngineCheck: entry.bookingEngineCheck,
            comment: entry.comment,
            isApproved: true,
            createdBy: entry.createdBy,
            createdAt: new Date(),
            updatedAt: new Date(),
          })
          .$returningId();

        finalId = result[0].id;
        logger.info(`[Blacklist] New entry approved: ${entry.name}`);
      }
    }

    // Удаляем pending запись (используем entry.id - первичный ключ найденной записи)
    await tx.delete(blacklistPending)
      .where(eq(blacklistPending.id, entry.id));

    // Удаляем соответствующую заявку из approvalRequests
    await tx.delete(approvalRequests)
      .where(and(
        eq(approvalRequests.entityType, 'blacklist'),
        eq(approvalRequests.targetId, entry.id)
      ));

    // Уведомляем об изменении - отправляем полные данные обновленной записи
    if (finalId) {
      const finalEntry = await tx.select()
        .from(blacklist)
        .where(eq(blacklist.id, finalId))
        .limit(1);

      if (finalEntry[0]) {
        await notifyBlacklistUpdate('blacklist:updated', mapToBlacklistEntry(finalEntry[0]), tx);
      }
    } else {
      // Для удаления отправляем fullRefresh
      await notifyBlacklistUpdate('blacklist:updated', { action: 'fullRefresh', count: 1 }, tx);
    }
  });
}

/**
 * Отклонить отдельную запись (Admin only)
 */
export async function rejectBlacklistEntry(pendingId: string): Promise<void> {
  // Проверяем существование записи - ищем по id или targetId
  const pendingEntries = await db.select()
    .from(blacklistPending)
    .where(or(
      eq(blacklistPending.id, pendingId),
      eq(blacklistPending.targetId, pendingId)
    ))
    .limit(1);

  if (!pendingEntries[0]) {
    throw new Error('Запись на согласование не найдена');
  }

  const entry = pendingEntries[0];

  // Используем транзакцию для атомарности
  await db.transaction(async (tx) => {
    // Удаляем pending запись (используем entry.id - первичный ключ найденной записи)
    await tx.delete(blacklistPending)
      .where(eq(blacklistPending.id, entry.id));

    // Удаляем соответствующую заявку из approvalRequests
    await tx.delete(approvalRequests)
      .where(and(
        eq(approvalRequests.entityType, 'blacklist'),
        eq(approvalRequests.targetId, entry.id)
      ));

    logger.info(`[Blacklist] Rejected pending entry: ${entry.id}`);

    // Уведомляем об изменении
    await notifyBlacklistUpdate('blacklist:updated', { action: 'fullRefresh', count: 1 }, tx);
  });
}

/**
 * Получить pending запись по ID
 * Используется для проверки прав в роутах воркфлоу
 */
export async function getBlacklistPendingEntryById(id: string): Promise<BlacklistPendingEntry | null> {
  const entries = await db.select({
    id: blacklistPending.id,
    name: blacklistPending.name,
    phone: blacklistPending.phone,
    bookingEngineCheck: blacklistPending.bookingEngineCheck,
    comment: blacklistPending.comment,
    status: blacklistPending.status,
    targetId: blacklistPending.targetId,
    createdBy: blacklistPending.createdBy,
    reviewedBy: blacklistPending.reviewedBy,
    reviewedAt: blacklistPending.reviewedAt,
    rejectionReason: blacklistPending.rejectionReason,
    createdAt: blacklistPending.createdAt,
    updatedAt: blacklistPending.updatedAt,
    creatorName: employees.fullName,
  })
    .from(blacklistPending)
    .leftJoin(employees, eq(blacklistPending.createdBy, employees.id))
    .where(or(
      eq(blacklistPending.id, id),
      eq(blacklistPending.targetId, id)
    ))
    .limit(1);

  if (!entries[0]) {
    return null;
  }

  return mapToBlacklistPendingEntry(entries[0]);
}
