/**
 * Обертка для создания безопасных GraphQL резолверов
 *
 * Обеспечивает автоматическую проверку прав доступа,
 * фильтрацию данных и аудит для резолверов
 */

import { OrganizationType, PrismaClient } from '@prisma/client'
import { GraphQLError } from 'graphql'

import { SecurityLogger } from '../../lib/security-logger'

import { CommercialDataAudit } from './commercial-data-audit'
import { ParticipantIsolation } from './participant-isolation'
import { SupplyDataFilter } from './supply-data-filter'
import { SecurityContext, CommercialAccessType, ResourceType } from './types'

/**
 * Опции для создания безопасного резолвера
 */
interface SecureResolverOptions {
  resourceType: ResourceType
  requiredRole?: OrganizationType[]
  auditAction: CommercialAccessType
  enableFiltering?: boolean
  enableAudit?: boolean
}

/**
 * Контекст GraphQL с добавленными полями безопасности
 */
interface GraphQLContext {
  user?: {
    id: string
    organizationId: string
    organizationType: OrganizationType
  }
  prisma: unknown // PrismaClient
  req?: {
    ip?: string
    headers?: Record<string, string>
    socket?: {
      remoteAddress?: string
    }
  }
}

/**
 * Создает безопасную обертку для GraphQL резолвера
 */
export function createSecureResolver<TArgs, TResult>(
  resolver: (parent: unknown, args: TArgs, context: GraphQLContext) => Promise<TResult>,
  options: SecureResolverOptions,
) {
  return async (parent: unknown, args: TArgs, context: GraphQLContext): Promise<TResult> => {
    const securityEnabled = process.env.ENABLE_SUPPLY_SECURITY === 'true'
    const auditEnabled = process.env.ENABLE_SECURITY_AUDIT === 'true'

    // Если система безопасности отключена - выполняем оригинальный резолвер
    if (!securityEnabled) {
      return resolver(parent, args, context)
    }

    // Проверка аутентификации
    if (!context.user) {
      throw new GraphQLError('Authentication required', {
        extensions: { code: 'UNAUTHENTICATED' },
      })
    }

    const securityContext: SecurityContext = {
      user: {
        id: context.user.id,
        organizationId: context.user.organizationId,
        organizationType: context.user.organizationType,
      },
      ipAddress: context.req?.ip || context.req?.socket?.remoteAddress,
      userAgent: context.req?.headers?.['user-agent'],
      request: {
        headers: context.req?.headers || {},
        timestamp: new Date(),
      },
    }

    try {
      // Проверка роли если требуется
      if (options.requiredRole && !options.requiredRole.includes(context.user.organizationType)) {
        // Логируем попытку несанкционированного доступа
        if (auditEnabled) {
          await CommercialDataAudit.logUnauthorizedAccess(context.prisma as any, {
            userId: context.user.id,
            organizationType: context.user.organizationType,
            resourceType: options.resourceType,
            resourceId: 'unknown',
            reason: `Insufficient role: ${context.user.organizationType}, required: ${options.requiredRole.join(', ')}`,
            ipAddress: securityContext.ipAddress,
            userAgent: securityContext.userAgent,
          })
        }

        throw new GraphQLError('Insufficient permissions', {
          extensions: { code: 'FORBIDDEN' },
        })
      }

      // Логирование доступа
      if (auditEnabled && options.enableAudit !== false) {
        await CommercialDataAudit.logAccess(context.prisma as any, {
          userId: securityContext.user.id,
          organizationType: securityContext.user.organizationType,
          action: options.auditAction,
          resourceType: options.resourceType,
          metadata: { args },
          ipAddress: securityContext.ipAddress,
          userAgent: securityContext.userAgent,
        })
      }

      // Выполнение оригинального резолвера
      let result = await resolver(parent, args, context)

      // Фильтрация результата если включена
      if (options.enableFiltering !== false && result) {
        result = await filterResolverResult(result, securityContext, options.resourceType)
      }

      return result
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'secureResolver',
        resourceType: options.resourceType,
        userId: context.user.id,
        organizationType: context.user.organizationType,
      })
      throw error
    }
  }
}

/**
 * Фильтрует результат резолвера в зависимости от типа данных
 */
async function filterResolverResult(
  result: unknown,
  context: SecurityContext,
  resourceType: ResourceType,
): Promise<unknown> {
  // Если это массив - фильтруем каждый элемент
  if (Array.isArray(result)) {
    return Promise.all(result.map((item) => filterSingleItem(item, context, resourceType)))
  }

  // Если это одиночный объект - фильтруем его
  return filterSingleItem(result, context, resourceType)
}

/**
 * Фильтрует одиночный элемент данных
 */
async function filterSingleItem(item: unknown, context: SecurityContext, resourceType: ResourceType): Promise<unknown> {
  switch (resourceType) {
    case 'SUPPLY_ORDER':
      // Фильтруем данные поставки
      if (item && typeof item === 'object' && (item as any).id) {
        const filtered = SupplyDataFilter.filterSupplyOrder(item as any, context)
        return filtered.data
      }
      break

    case 'PRODUCT':
    case 'SERVICE':
    case 'CONSUMABLE':
      // Для других типов ресурсов - пока возвращаем как есть
      // TODO: добавить специфичную фильтрацию
      break
  }

  return item
}

/**
 * Декоратор для автоматического создания безопасного резолвера
 */
export function SecureResolver(options: SecureResolverOptions) {
  return function (_target: unknown, _propertyName: string, descriptor: PropertyDescriptor) {
    const method = descriptor.value

    descriptor.value = createSecureResolver(method, options)
  }
}

/**
 * Вспомогательные функции для проверки доступа
 */
export const SecurityHelpers = {
  /**
   * Проверяет доступ к заказу поставки
   */
  async checkSupplyOrderAccess(prisma: unknown, orderId: string, context: SecurityContext): Promise<boolean> {
    return ParticipantIsolation.validateSupplyOrderAccess(prisma as PrismaClient, orderId, context)
  },

  /**
   * Проверяет партнерские отношения
   */
  async checkPartnershipAccess(
    prisma: unknown,
    organizationId: string,
    partnerId: string,
    context: SecurityContext,
  ): Promise<boolean> {
    return ParticipantIsolation.validatePartnerAccess(prisma as PrismaClient, organizationId, partnerId, context)
  },
}
