// Оптимизированные утилиты авторизации для устранения N+1 проблем
import { GraphQLError } from 'graphql'

import { prisma } from '../../../lib/prisma'
import { Context } from '../../context'

// Кеш для пользователей в рамках одного запроса
const userCache = new Map<string, any>()

// Очистка кеша в начале каждого GraphQL запроса
export const clearUserCache = () => {
  userCache.clear()
}

/**
 * Оптимизированное получение пользователя с кешированием
 * Устраняет N+1 проблему при множественных проверках пользователя
 */
export const getCurrentUser = async (context: Context) => {
  if (!context.user) {
    throw new GraphQLError('Требуется авторизация', {
      extensions: { code: 'UNAUTHENTICATED' },
    })
  }

  const userId = context.user.id
  
  // Проверяем кеш
  if (userCache.has(userId)) {
    return userCache.get(userId)
  }

  // Загружаем пользователя с организацией
  const user = await prisma.user.findUnique({
    where: { id: userId },
    include: { 
      organization: {
        select: {
          id: true,
          name: true,
          type: true,
          fullName: true,
        },
      },
    },
  })

  if (!user) {
    throw new GraphQLError('Пользователь не найден', {
      extensions: { code: 'UNAUTHENTICATED' },
    })
  }

  if (!user.organization) {
    throw new GraphQLError('Пользователь не привязан к организации', {
      extensions: { code: 'FORBIDDEN' },
    })
  }

  // Кешируем результат
  userCache.set(userId, user)
  
  return user
}

/**
 * Проверка доступа к фулфилмент функциям
 */
export const requireFulfillmentAccess = async (context: Context) => {
  const user = await getCurrentUser(context)

  if (user.organization?.type !== 'FULFILLMENT') {
    throw new GraphQLError('Только фулфилмент может выполнять эти операции', {
      extensions: { code: 'FORBIDDEN' },
    })
  }

  return user
}

/**
 * Проверка доступа к поставщик функциям
 */
export const requireWholesaleAccess = async (context: Context) => {
  const user = await getCurrentUser(context)

  if (user.organization?.type !== 'WHOLESALE') {
    throw new GraphQLError('Только поставщики могут выполнять эти операции', {
      extensions: { code: 'FORBIDDEN' },
    })
  }

  return user
}

/**
 * Проверка доступа к селлер функциям
 */
export const requireSellerAccess = async (context: Context) => {
  const user = await getCurrentUser(context)

  if (user.organization?.type !== 'SELLER') {
    throw new GraphQLError('Только селлеры могут выполнять эти операции', {
      extensions: { code: 'FORBIDDEN' },
    })
  }

  return user
}

/**
 * Проверка доступа к логистика функциям
 */
export const requireLogisticsAccess = async (context: Context) => {
  const user = await getCurrentUser(context)

  if (user.organization?.type !== 'LOGIST') {
    throw new GraphQLError('Только логистика может выполнять эти операции', {
      extensions: { code: 'FORBIDDEN' },
    })
  }

  return user
}

/**
 * Универсальная проверка типа организации
 */
export const requireOrganizationType = async (context: Context, types: string[]) => {
  const user = await getCurrentUser(context)

  if (!types.includes(user.organization?.type || '')) {
    throw new GraphQLError(`Операция доступна только для: ${types.join(', ')}`, {
      extensions: { code: 'FORBIDDEN' },
    })
  }

  return user
}

/**
 * HOC для оборачивания резолверов с авторизацией
 */
export const withAuth = <T>(resolver: (parent: any, args: any, context: Context) => Promise<T>) => {
  return async (parent: any, args: any, context: Context): Promise<T> => {
    // Проверяем базовую авторизацию
    await getCurrentUser(context)
    
    // Вызываем исходный резолвер
    return resolver(parent, args, context)
  }
}

/**
 * HOC для резолверов с проверкой типа организации
 */
export const withOrgTypeAuth = <T>(
  types: string[], 
  resolver: (parent: any, args: any, context: Context, user: any) => Promise<T>,
) => {
  return async (parent: any, args: any, context: Context): Promise<T> => {
    const user = await requireOrganizationType(context, types)
    return resolver(parent, args, context, user)
  }
}