// src/server/features/blacklist/lib/blacklist.service.ts
// Business logic for blacklist management with approval workflow

import { db } from '@serverShared/db/client';
import { blacklist, blacklistPending } from '../db/blacklist.table';
import { employees } from '@serverShared/db/schema';
import { eq, and, or, isNull } from 'drizzle-orm';
import { mapToBlacklistEntry, mapPendingToBlacklistEntryWithMetadata, mapToBlacklistPendingEntry } from './blacklist.mapper';
import { createDraft, updateDraft, deleteRequest } from './blacklist.drafts';
import { executeCreate, executeUpdate, executeDelete, autoApproveDraft, updatePendingDraft, deletePendingDraft } from './blacklist.core';
import type { BlacklistEntry, BlacklistPendingEntry, BlacklistEntryWithMetadata, CreateBlacklistRequest, UpdateBlacklistRequest } from '../blacklist.schema';
import type { UserInfo } from '@serverShared/lib/auth';
import { AppPermission } from '@shared/contracts/permissions';
import { hasPermission, hasAnyPermission } from '@serverShared/plugins/rbac';

/**
 * Создать запись в черном списке
 */
export async function createBlacklistEntry(request: Omit<CreateBlacklistRequest, 'createdBy'>, user: UserInfo): Promise<BlacklistEntryWithMetadata> {
  const canDirectEdit = await hasPermission(user, AppPermission.BLACKLIST_EDIT);
  const canRequestApproval = await hasPermission(user, AppPermission.BLACKLIST_REQUEST_APPROVAL);
  const canCreate = await hasAnyPermission(user, [AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL]);

  if (!canCreate) {
    throw new Error('У вас нет прав на создание записей черного списка');
  }

  const requestWithUser: CreateBlacklistRequest = {
    ...request,
    createdBy: user.id,
  };

  // Прямое создание в таблице blacklist
  if (canDirectEdit) {
    return executeCreate(requestWithUser);
  }

  if (!canRequestApproval) {
    throw new Error('У вас нет прав на создание заявки на согласование');
  }

  // Создаем черновик на согласование
  return createDraft(requestWithUser);
}

/**
 * Обновить запись в черном списке
 */
export async function updateBlacklistEntry(
  request: Omit<UpdateBlacklistRequest, 'updatedBy'>,
  user: UserInfo
): Promise<BlacklistEntryWithMetadata> {
  const { id, name, phone, bookingEngineCheck, comment } = request;
  const canDirectEdit = await hasPermission(user, AppPermission.BLACKLIST_EDIT);
  const canRequestApproval = await hasPermission(user, AppPermission.BLACKLIST_REQUEST_APPROVAL);
  const canUpdate = await hasAnyPermission(user, [AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL]);

  if (!canUpdate) {
    throw new Error('У вас нет прав на изменение записей черного списка');
  }

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

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

  // 2. Если нет в основной, ищем в таблице черновиков blacklist_pending
  if (!existing[0]) {
    const pendingExisting = await db.select()
      .from(blacklistPending)
      .where(eq(blacklistPending.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 (phone !== undefined && phone.trim().length === 0) {
    throw new Error('Телефон обязателен');
  }

  // 4. Если это ПРЯМОЕ обновление черновика (Админом или владельцем черновика)
  if (isPendingRecord) {
    // Пользователь с EDIT: автоматическое согласование при редактировании черновика
    if (canDirectEdit) {
      const updates: any = {
        updatedAt: new Date(),
      };
      if (name !== undefined) updates.name = name.trim();
      if (phone !== undefined) updates.phone = phone.trim();
      if (bookingEngineCheck !== undefined) updates.bookingEngineCheck = bookingEngineCheck;
      if (comment !== undefined) updates.comment = comment.trim() || null;

      return autoApproveDraft(id, pendingRecord!, user.id, updates);
    }

    if (!canRequestApproval) {
      throw new Error('У вас нет прав на создание заявки на согласование');
    }

    if (pendingRecord?.createdBy !== user.id) {
      throw new Error('У вас нет прав на редактирование этого черновика');
    }

    // Обычное обновление черновика (без согласования)
    const updates: any = { updatedAt: new Date() };
    if (name !== undefined) updates.name = name.trim();
    if (phone !== undefined) updates.phone = phone.trim();
    if (bookingEngineCheck !== undefined) updates.bookingEngineCheck = bookingEngineCheck;
    if (comment !== undefined) updates.comment = comment.trim() || null;

    return updatePendingDraft(id, updates);
  }

  // 5. Дальше идет старая логика для записей из основной таблицы (blacklist)
  
  // Прямое обновление в таблице blacklist
  if (canDirectEdit) {
    return executeUpdate({ ...request, updatedBy: user.id });
  }

  if (!canRequestApproval) {
    throw new Error('У вас нет прав на создание заявки на согласование');
  }

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

/**
 * Удалить запись из черного списка (soft delete)
 */
export async function deleteBlacklistEntry(id: string, user: UserInfo): Promise<void> {
  const canDirectEdit = await hasPermission(user, AppPermission.BLACKLIST_EDIT);
  const canRequestApproval = await hasPermission(user, AppPermission.BLACKLIST_REQUEST_APPROVAL);
  const canDelete = await hasAnyPermission(user, [AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL]);

  if (!canDelete) {
    throw new Error('У вас нет прав на удаление записей черного списка');
  }

  // 1. Сначала ищем в основной таблице blacklist
  let existing = await db.select()
    .from(blacklist)
    .where(eq(blacklist.id, id))
    .limit(1);

  let isPendingRecord = false;

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

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

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

  // 4. Если это удаление черновика (еще не одобренной записи)
  if (isPendingRecord) {
    if (!canDirectEdit && (existing[0] as any).createdBy !== user.id) {
      throw new Error('У вас нет прав на удаление этого черновика');
    }

    return deletePendingDraft(id);
  }

  // 5. Прямое удаление из таблицы blacklist
  if (canDirectEdit) {
    return executeDelete(id);
  }

  if (!canRequestApproval) {
    throw new Error('У вас нет прав на создание заявки на удаление');
  }

  // 6. Создаем запрос на удаление
  return deleteRequest(id, existing[0], user.id);
}

/**
 * Получить все записи черного списка (только одобренные)
 */
export async function getAllBlacklistEntries(): Promise<BlacklistEntry[]> {
  const entries = await db.select()
    .from(blacklist)
    .where(isNull(blacklist.archivedAt))
    .orderBy(blacklist.createdAt);

  return entries.map(mapToBlacklistEntry);
}

/**
 * Получить записи по статусу согласования
 */
export async function getBlacklistEntriesByStatus(status: 'pending' | 'approved'): Promise<BlacklistEntry[]> {
  const isApproved = status === 'approved';

  const entries = await db.select()
    .from(blacklist)
    .where(and(
      eq(blacklist.isApproved, isApproved),
      isNull(blacklist.archivedAt)
    ))
    .orderBy(blacklist.createdAt);

  return entries.map(mapToBlacklistEntry);
}

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

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

  return mapToBlacklistEntry(entry[0]);
}

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

  return entries.map(mapToBlacklistPendingEntry);
}
