// src/server/features/reference_books/contractors.service.ts
// Business logic for contractors management with approval workflow

import { db } from '../../shared/db/client';
import { contractors, contractorsPending } from './db/contractors.table';
import { employees } from '../personnel/db/employees.table';
import { eq, and, or, isNull, isNotNull } from 'drizzle-orm';
import { mapToContractorEntry, mapToContractorPendingEntry, mapPendingToContractorEntryWithMetadata } from './lib/contractors.mapper';
import { createDraft, updateDraft, deleteRequest } from './lib/contractors.drafts';
import { executeCreate, executeUpdate, executeDelete, autoApproveDraft, updatePendingDraft, deletePendingDraft } from './lib/contractors.core';
import type { ContractorPendingEntry, ContractorEntryWithMetadata, CreateContractorRequestWithAdmin, UpdateContractorRequestWithAdmin, CreateContractorRequest, UpdateContractorRequest } from './contractors.schema';

// Type for contractor entry
type ContractorEntry = typeof contractors.$inferSelect;

/**
 * Создать запись в списке контрагентов
 */
export async function createContractorEntry(request: CreateContractorRequestWithAdmin): Promise<ContractorEntryWithMetadata> {
  // АДМИН: Прямое создание в таблице contractors
  if (request.isAdmin) {
    return executeCreate(request);
  }

  // МЕНЕДЖЕР: Создаем черновик на согласование
  return createDraft(request);
}

/**
 * Обновить запись в списке контрагентов
 */
export async function updateContractorEntry(
  id: string,
  request: Omit<UpdateContractorRequestWithAdmin, 'id'>
): Promise<ContractorEntryWithMetadata> {
  const { name, category, contractNumber, phone, email, webLink, managerName, managerPhone, managerEmail, comment, updatedBy, isAdmin } = request;

  // 1. Пытаемся найти в основной таблице contractors
  let existing = await db.select()
    .from(contractors)
    .where(eq(contractors.id, id))
    .limit(1);

  let isPendingRecord = false;
  let pendingRecord: typeof contractorsPending.$inferSelect | null = null;

  // 2. Если нет в основной, ищем в таблице черновиков contractors_pending
  if (!existing[0]) {
    const pendingExisting = await db.select()
      .from(contractorsPending)
      .where(eq(contractorsPending.id, id))
      .limit(1);

    if (pendingExisting[0]) {
      existing = pendingExisting as any; // Мапим для совместимости проверок
      pendingRecord = pendingExisting[0]; // Сохраняем оригинальную pending запись
      isPendingRecord = true;
    }
  }

  // 3. Если не нашли ни там, ни там — кидаем ошибку
  if (!existing[0]) {
    throw new Error('Запись не найдена');
  }

  // Валидация полей
  if (name !== undefined && name.trim().length === 0) {
    throw new Error('Название обязательно');
  }
  if (category !== undefined && category.trim().length === 0) {
    throw new Error('Категория обязательна');
  }
  if (phone !== undefined && phone.trim().length === 0) {
    throw new Error('Телефон обязателен');
  }

  // 4. Если это ПРЯМОЕ обновление черновика (Админом или владельцем черновика)
  if (isPendingRecord) {
    // АДМИН: Автоматическое согласование при редактировании черновика
    if (isAdmin) {
      const updates: any = {
        updatedAt: new Date(),
      };
      if (name !== undefined) updates.name = name.trim();
      if (category !== undefined) updates.category = category.trim();
      if (contractNumber !== undefined) updates.contractNumber = contractNumber?.trim() || null;
      if (phone !== undefined) updates.phone = phone.trim();
      if (email !== undefined) updates.email = email?.trim() || null;
      if (webLink !== undefined) updates.webLink = webLink?.trim() || null;
      if (managerName !== undefined) updates.managerName = managerName?.trim() || null;
      if (managerPhone !== undefined) updates.managerPhone = managerPhone?.trim() || null;
      if (managerEmail !== undefined) updates.managerEmail = managerEmail?.trim() || null;
      if (comment !== undefined) updates.comment = comment?.trim() || null;

      return autoApproveDraft(id, pendingRecord!, updatedBy!, updates);
    }

    // МЕНЕДЖЕР: Обычное обновление черновика (без согласования)
    const updates: any = { updatedAt: new Date() };
    if (name !== undefined) updates.name = name.trim();
    if (category !== undefined) updates.category = category.trim();
    if (contractNumber !== undefined) updates.contractNumber = contractNumber?.trim() || null;
    if (phone !== undefined) updates.phone = phone.trim();
    if (email !== undefined) updates.email = email?.trim() || null;
    if (webLink !== undefined) updates.webLink = webLink?.trim() || null;
    if (managerName !== undefined) updates.managerName = managerName?.trim() || null;
    if (managerPhone !== undefined) updates.managerPhone = managerPhone?.trim() || null;
    if (managerEmail !== undefined) updates.managerEmail = managerEmail?.trim() || null;
    if (comment !== undefined) updates.comment = comment?.trim() || null;

    return updatePendingDraft(id, updates);
  }

  // 5. Дальше идет старая логика для записей из основной таблицы (contractors)

  // АДМИН: Прямое обновление в таблице contractors
  if (isAdmin) {
    return executeUpdate(id, request);
  }

  // МЕНЕДЖЕР: Создаем черновик на согласование
  return updateDraft({ id, ...request }, existing[0]);
}

/**
 * Удалить запись из списка контрагентов (soft delete)
 */
export async function deleteContractorEntry(id: string, deletedBy: string, isAdmin: boolean): Promise<void> {
  // 1. Сначала ищем в основной таблице contractors
  let existing = await db.select()
    .from(contractors)
    .where(eq(contractors.id, id))
    .limit(1);

  let isPendingRecord = false;

  // 2. Если не найдено в основной таблице, ищем в contractors_pending
  if (!existing[0]) {
    const pendingExisting = await db.select()
      .from(contractorsPending)
      .where(eq(contractorsPending.id, id))
      .limit(1);

    if (pendingExisting[0]) {
      existing = pendingExisting as any; // Мапим для совместимости
      isPendingRecord = true;
    }
  }

  // 3. Если не нашли ни там, ни там — кидаем ошибку
  if (!existing[0]) {
    throw new Error('Запись не найдена');
  }

  // 4. Если это удаление черновика (еще не одобренной записи)
  if (isPendingRecord) {
    return deletePendingDraft(id);
  }

  // 5. АДМИН: Архивация записи из таблицы contractors
  if (isAdmin) {
    return executeDelete(id);
  }

  // 6. МЕНЕДЖЕР: Создаем запрос на удаление
  return deleteRequest(id, existing[0], deletedBy);
}

/**
 * Получить все записи контрагентов
 * @param archived - если false, возвращает только активные записи; если true, возвращает только архивные; если undefined, возвращает только активные записи
 * @param status - 'all' (все записи), 'pending' (только на согласовании), 'approved' (только одобренные), undefined (только одобренные)
 */
export async function getAllContractorsEntries(archived?: boolean, status?: 'all' | 'pending' | 'approved'): Promise<ContractorEntryWithMetadata[]> {
  // Если запрашиваем только pending записи - используем отдельную функцию
  if (status === 'pending') {
    const pendingEntries = await getContractorsPendingEntries();
    // Преобразуем ContractorPendingEntry в ContractorEntryWithMetadata
    return pendingEntries.map(entry => ({
      id: entry.id,
      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: false,
      status: entry.status,
      targetId: entry.targetId,
      createdBy: entry.createdBy,
      creatorName: entry.creatorName,
      reviewedBy: entry.reviewedBy,
      reviewedAt: entry.reviewedAt,
      rejectionReason: entry.rejectionReason,
      createdAt: entry.createdAt,
      updatedAt: entry.updatedAt,
      archivedAt: null, // Pending записи не архивируются
    }));
  }

  // Формируем условие для фильтрации по archivedAt
  const archivedCondition = archived === true
    ? isNotNull(contractors.archivedAt) // archivedAt IS NOT NULL
    : isNull(contractors.archivedAt); // archivedAt IS NULL (по умолчанию)

  // Формируем условие для фильтрации по isApproved
  const approvedCondition = eq(contractors.isApproved, true);

  // Получаем одобренные записи из основной таблицы
  const whereCondition = and(archivedCondition, approvedCondition);

  const approvedEntries = await db.select()
    .from(contractors)
    .where(whereCondition)
    .orderBy(contractors.createdAt);

  // Если запрашиваем все записи (all), добавляем pending записи
  if (status === 'all') {
    const pendingEntries = await getContractorsPendingEntries();
    const mappedPendingEntries = pendingEntries.map(entry => ({
      id: entry.id,
      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: false,
      status: entry.status,
      targetId: entry.targetId,
      createdBy: entry.createdBy,
      creatorName: entry.creatorName,
      reviewedBy: entry.reviewedBy,
      reviewedAt: entry.reviewedAt,
      rejectionReason: entry.rejectionReason,
      createdAt: entry.createdAt,
      updatedAt: entry.updatedAt,
      archivedAt: null, // Pending записи не архивируются
    }));

    // Объединяем и сортируем по createdAt
    return [...mappedPendingEntries, ...approvedEntries.map(mapToContractorEntry)]
      .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
  }

  // Если запрашиваем только одобренные (approved/undefined)
  return approvedEntries.map(mapToContractorEntry);
}

/**
 * Получить запись по ID
 */
export async function getContractorEntryById(id: string): Promise<ContractorEntryWithMetadata | null> {
  const entry = await db.select()
    .from(contractors)
    .where(eq(contractors.id, id))
    .limit(1);

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

  return mapToContractorEntry(entry[0]);
}

/**
 * Получить все записи из таблицы contractors_pending со статусом pending или to_delete
 */
export async function getContractorsPendingEntries(): Promise<ContractorPendingEntry[]> {
  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.status, 'pending'),
      eq(contractorsPending.status, 'to_delete')
    ))
    .orderBy(contractorsPending.createdAt);

  return entries.map(mapToContractorPendingEntry);
}
