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

import { db } from '../../../shared/db/client';
import { contractors, contractorsPending } from '../db/contractors.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 { mapToContractorEntry, mapToContractorPendingEntry } from './contractors.mapper';
import { notifyContractorsUpdate } from './contractors.notifications';
import type { ContractorPendingEntry } from '../contractors.schema';

/**
 * Согласовать отдельную запись (Admin only)
 * Переносит данные из contractors_pending в основную таблицу contractors
 */
export async function approveContractorEntry(pendingId: string): Promise<void> {
  // Находим pending запись - ищем по id или targetId
  const pendingEntries = await db.select()
    .from(contractorsPending)
    .where(or(
      eq(contractorsPending.id, pendingId),
      eq(contractorsPending.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, архивируем оригинальную запись (soft delete)
      if (entry.targetId) {
        await tx.update(contractors)
          .set({
            archivedAt: new Date(),
            updatedAt: new Date(),
          })
          .where(eq(contractors.id, entry.targetId));

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

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

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

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

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

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

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

/**
 * Отклонить отдельную запись (Admin and MANAGER for own requests)
 * Просто удаляет запись из contractors_pending и approval_requests
 */
export async function rejectContractorEntry(pendingId: string): Promise<void> {
  // Проверяем существование записи - ищем по id или targetId
  const pendingEntries = await db.select()
    .from(contractorsPending)
    .where(or(
      eq(contractorsPending.id, pendingId),
      eq(contractorsPending.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(contractorsPending)
      .where(eq(contractorsPending.id, entry.id));

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

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

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

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

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

  return mapToContractorPendingEntry(entries[0]);
}
