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

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

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

import { SecurityContext, GroupedLogisticsOrder } from './types'

interface SupplyOrder {
  id: string
  organizationId: string
  fulfillmentCenterId?: string
  logisticsPartnerId?: string
  packagesCount?: number
  volume?: number
  route: {
    from: string
    to: string
  }
}

export class ParticipantIsolation {
  /**
   * Проверяет изоляцию данных между селлерами
   * Селлеры не должны видеть данные друг друга
   */
  static async validateSellerIsolation(
    prisma: PrismaClient,
    currentUserId: string,
    targetSellerId: string,
    context?: SecurityContext,
  ): Promise<boolean> {
    // Селлер может видеть только свои данные
    if (currentUserId !== targetSellerId) {
      // Логируем попытку несанкционированного доступа
      if (context && context.user && FEATURE_FLAGS.SUPPLY_DATA_SECURITY.auditEnabled) {
        SecurityLogger.logAccessAttempt({
          userId: context.user.id,
          organizationType: context.user.organizationType,
          resource: 'SELLER_DATA',
          resourceId: targetSellerId,
          success: false,
          reason: 'Seller isolation violation',
          ipAddress: context.ipAddress,
          userAgent: context.userAgent,
        })
      }

      throw new GraphQLError('Access denied to other seller data', {
        extensions: {
          code: 'FORBIDDEN',
          reason: 'SELLER_ISOLATION',
        },
      })
    }

    return true
  }

  /**
   * Проверяет доступ к данным через партнерские отношения
   */
  static async validatePartnerAccess(
    prisma: PrismaClient,
    organizationId: string,
    partnerId: string,
    context?: SecurityContext,
  ): Promise<boolean> {
    try {
      // Проверяем активное партнерство
      const partnership = await prisma.counterparty.findFirst({
        where: {
          OR: [
            {
              organizationId: organizationId,
              counterpartyId: partnerId,
            },
            {
              organizationId: partnerId,
              counterpartyId: organizationId,
            },
          ],
        },
        select: {
          id: true,
          createdAt: true,
        },
      })

      if (!partnership) {
        // Логируем попытку доступа без партнерства
        if (context && context.user && FEATURE_FLAGS.SUPPLY_DATA_SECURITY.auditEnabled) {
          SecurityLogger.logAccessAttempt({
            userId: context.user.id,
            organizationType: context.user.organizationType,
            resource: 'PARTNERSHIP_DATA',
            resourceId: partnerId,
            success: false,
            reason: 'No active partnership found',
            ipAddress: context.ipAddress,
            userAgent: context.userAgent,
          })
        }

        throw new GraphQLError('No active partnership found', {
          extensions: {
            code: 'FORBIDDEN',
            reason: 'NO_PARTNERSHIP',
          },
        })
      }

      // Логируем успешную проверку партнерства
      if (context && context.user && FEATURE_FLAGS.SUPPLY_DATA_SECURITY.auditEnabled) {
        SecurityLogger.logAccessAttempt({
          userId: context.user.id,
          organizationType: context.user.organizationType,
          resource: 'PARTNERSHIP_DATA',
          resourceId: partnerId,
          success: true,
          ipAddress: context.ipAddress,
          userAgent: context.userAgent,
        })
      }

      return true
    } catch (error) {
      if (context && context.user) {
        SecurityLogger.logSecurityError(error as Error, {
          operation: 'validatePartnerAccess',
          organizationId,
          partnerId,
          userId: context.user.id,
        })
      }
      throw error
    }
  }

  /**
   * Проверяет доступ к поставке через роль в системе
   */
  static async validateSupplyOrderAccess(
    prisma: PrismaClient,
    supplyOrderId: string,
    context: SecurityContext,
  ): Promise<boolean> {
    try {
      if (!context.user) {
        throw new GraphQLError('User context required for access validation')
      }
      const { organizationType, organizationId } = context.user

      // Получаем базовую информацию о заказе
      const supplyOrder = await prisma.supplyOrder.findUnique({
        where: { id: supplyOrderId },
        select: {
          id: true,
          organizationId: true,
          fulfillmentCenterId: true,
          logisticsPartnerId: true,
          items: {
            select: {
              product: {
                select: {
                  organizationId: true,
                },
              },
            },
          },
        },
      })

      if (!supplyOrder) {
        throw new GraphQLError('Supply order not found', {
          extensions: { code: 'NOT_FOUND' },
        })
      }

      let hasAccess = false
      let accessReason = ''

      switch (organizationType) {
        case 'SELLER':
          hasAccess = supplyOrder.organizationId === organizationId
          accessReason = hasAccess ? 'Order owner' : 'Not order owner'
          break

        case 'WHOLESALE':
          // Поставщик имеет доступ если есть его товары в заказе
          hasAccess = supplyOrder.items.some((item) => item.product.organizationId === organizationId)
          accessReason = hasAccess ? 'Has products in order' : 'No products in order'
          break

        case 'FULFILLMENT':
          hasAccess = supplyOrder.fulfillmentCenterId === organizationId
          accessReason = hasAccess ? 'Assigned fulfillment' : 'Not assigned fulfillment'
          break

        case 'LOGIST':
          hasAccess = supplyOrder.logisticsPartnerId === organizationId
          accessReason = hasAccess ? 'Assigned logistics' : 'Not assigned logistics'
          break
      }

      // Логируем результат проверки
      if (FEATURE_FLAGS.SUPPLY_DATA_SECURITY.auditEnabled) {
        SecurityLogger.logAccessAttempt({
          userId: context.user.id,
          organizationType: context.user.organizationType,
          resource: 'SUPPLY_ORDER',
          resourceId: supplyOrderId,
          success: hasAccess,
          reason: accessReason,
          ipAddress: context.ipAddress,
          userAgent: context.userAgent,
        })
      }

      if (!hasAccess) {
        throw new GraphQLError('Access denied to this supply order', {
          extensions: {
            code: 'FORBIDDEN',
            reason: accessReason,
          },
        })
      }

      return true
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'validateSupplyOrderAccess',
        supplyOrderId,
        userId: context.user.id,
        organizationType: context.user.organizationType,
      })
      throw error
    }
  }

  /**
   * Группировка заказов для логистики с изоляцией селлеров
   * Логистика видит только маршруты и объемы, без коммерческой информации
   */
  static groupOrdersForLogistics(orders: SupplyOrder[]): GroupedLogisticsOrder[] {
    const grouped = orders.reduce(
      (acc, order) => {
        const routeKey = `${order.route.from}-${order.route.to}`

        if (!acc[routeKey]) {
          acc[routeKey] = {
            route: {
              from: order.route.from,
              to: order.route.to,
            },
            orders: [],
            totalPackages: 0,
            totalVolume: 0,
          }
        }

        // Добавляем заказ БЕЗ информации о селлере и коммерческих данных
        acc[routeKey].orders.push({
          id: order.id,
          packagesCount: order.packagesCount || 0,
          volume: order.volume || 0,
          // НЕ включаем: organizationId, sellerId, productDetails, prices
        })

        acc[routeKey].totalPackages += order.packagesCount || 0
        acc[routeKey].totalVolume += order.volume || 0

        return acc
      },
      {} as Record<string, GroupedLogisticsOrder>,
    )

    return Object.values(grouped)
  }

  /**
   * Валидация доступа к контрагенту
   */
  static async validateCounterpartyAccess(
    prisma: PrismaClient,
    requestingOrgId: string,
    targetOrgId: string,
    context?: SecurityContext,
  ): Promise<boolean> {
    // Организация может видеть себя
    if (requestingOrgId === targetOrgId) {
      return true
    }

    // Проверяем партнерство для доступа к ограниченной информации
    try {
      await this.validatePartnerAccess(prisma, requestingOrgId, targetOrgId, context)
      return true
    } catch {
      // Если нет партнерства - доступа нет
      return false
    }
  }

  /**
   * Проверка массового доступа к данным (защита от скрейпинга)
   */
  static async checkBulkAccessPattern(
    userId: string,
    action: string,
    timeWindowMs = 3600000, // 1 час
    threshold = 100,
    context?: SecurityContext,
  ): Promise<boolean> {
    // TODO: Реализовать через Redis или память для подсчета запросов
    // Пока заглушка для демонстрации логики

    const requestCount = await this.getRequestCount(userId, action, timeWindowMs)

    if (requestCount > threshold) {
      SecurityLogger.logSuspiciousActivity({
        userId,
        organizationType: context?.user?.organizationType || context?.organizationType || 'SELLER',
        activity: action,
        count: requestCount,
        timeframe: `${timeWindowMs / 1000}s`,
        threshold,
      })

      throw new GraphQLError('Too many requests. Please slow down.', {
        extensions: {
          code: 'TOO_MANY_REQUESTS',
          retryAfter: Math.ceil(timeWindowMs / 1000),
        },
      })
    }

    return true
  }

  /**
   * Общий метод валидации доступа
   */
  static async validateAccess(
    prisma: PrismaClient,
    organizationId: string,
    organizationType: string,
    resourceType: string,
  ): Promise<boolean> {
    // Базовые проверки доступа для поставщиков
    if (organizationType === 'WHOLESALE' && resourceType === 'SUPPLY_ORDER') {
      // Поставщики могут работать с заказами поставок
      return true
    }

    // Другие типы организаций и ресурсов
    if (['SELLER', 'FULFILLMENT', 'LOGIST'].includes(organizationType)) {
      return true
    }

    // По умолчанию блокируем доступ
    throw new GraphQLError('Access denied for organization type', {
      extensions: { code: 'ACCESS_DENIED' },
    })
  }

  /**
   * Заглушка для подсчета запросов (заменить на реальную реализацию)
   */
  private static async getRequestCount(_userId: string, _action: string, _timeWindowMs: number): Promise<number> {
    // TODO: Реализовать через Redis или базу данных
    // Возвращаем случайное число для демонстрации
    return Math.floor(Math.random() * 150)
  }
}
