/**
 * Контроль доступа к рецептурам товаров
 *
 * Управляет видимостью производственных секретов (рецептур) между участниками.
 * Рецептуры видят только селлеры и назначенные фулфилмент-центры.
 */

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

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

/**
 * Интерфейс рецептуры товара
 */
interface ProductRecipe {
  id: string
  productId: string
  services: Array<{
    id: string
    name: string
    organizationId: string
    price?: number
    pricePerUnit?: number
  }>
  fulfillmentConsumables: Array<{
    id: string
    name: string
    quantity: number
    organizationId: string
    pricePerUnit?: number
    price?: number
  }>
  sellerConsumables: Array<{
    id: string
    name: string
    quantity: number
    organizationId: string
    pricePerUnit?: number
    price?: number
  }>
}

/**
 * Отфильтрованная рецептура
 */
interface FilteredRecipe {
  services: Array<{
    id: string
    name: string
    organizationId?: string
    price?: number
    pricePerUnit?: number
  }>
  fulfillmentConsumables: Array<{
    id: string
    name: string
    quantity: number
    organizationId?: string
    pricePerUnit?: number
    price?: number
  }>
  sellerConsumables: Array<{
    id: string
    name: string
    quantity: number
    organizationId?: string
    pricePerUnit?: number
    price?: number
  }>
}

export class RecipeAccessControl {
  /**
   * Фильтрует рецептуру в зависимости от роли и прав доступа
   */
  static filterRecipeByRole(
    recipe: ProductRecipe,
    userRole: OrganizationType,
    userOrgId: string,
    context?: {
      fulfillmentId?: string
      sellerOrgId?: string
      supplyOrderId?: string
    },
  ): FilteredRecipe | null {
    const startTime = Date.now()

    try {
      let filteredRecipe: FilteredRecipe | null = null
      let accessGranted = false

      switch (userRole) {
        case 'SELLER':
          filteredRecipe = this.filterForSeller(recipe, userOrgId, context?.sellerOrgId)
          accessGranted = true
          break

        case 'FULFILLMENT':
          filteredRecipe = this.filterForFulfillment(recipe, userOrgId, context?.fulfillmentId)
          accessGranted = filteredRecipe !== null
          break

        case 'WHOLESALE':
          // Поставщики НЕ видят рецептуру - это коммерческая тайна
          filteredRecipe = null
          accessGranted = false
          break

        case 'LOGIST':
          // Логистика НЕ видит рецептуру - только маршруты
          filteredRecipe = null
          accessGranted = false
          break

        default:
          filteredRecipe = null
          accessGranted = false
      }

      // Логирование доступа к рецептуре
      if (FEATURE_FLAGS.SUPPLY_DATA_SECURITY.auditEnabled) {
        SecurityLogger.logDataAccess({
          userId: userOrgId,
          organizationType: userRole,
          action: 'VIEW_RECIPE',
          resource: 'PRODUCT_RECIPE',
          resourceId: recipe.id,
          filtered: filteredRecipe !== recipe,
          removedFields: this.getRemovedFields(recipe, filteredRecipe),
        })

        // Логирование производственных секретов
        if (accessGranted) {
          SecurityLogger.logFilteringPerformance({
            operation: 'filterRecipeByRole',
            duration: Date.now() - startTime,
            recordsFiltered: 1,
            fieldsRemoved: this.getRemovedFields(recipe, filteredRecipe).length,
            cacheHit: false,
          })
        }
      }

      return filteredRecipe
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'filterRecipeByRole',
        recipeId: recipe.id,
        userRole,
        userOrgId,
      })
      throw error
    }
  }

  /**
   * SELLER видит полную рецептуру своих товаров
   */
  private static filterForSeller(recipe: ProductRecipe, userOrgId: string, sellerOrgId?: string): FilteredRecipe {
    // Проверяем, что рецептура принадлежит данному селлеру
    if (sellerOrgId && sellerOrgId !== userOrgId) {
      throw new GraphQLError('Access denied to recipe of other seller', {
        extensions: { code: 'FORBIDDEN' },
      })
    }

    // Селлер видит полную рецептуру
    return {
      services: recipe.services.map((s) => ({
        id: s.id,
        name: s.name,
        organizationId: s.organizationId,
        price: s.price,
        pricePerUnit: s.pricePerUnit,
      })),
      fulfillmentConsumables: recipe.fulfillmentConsumables.map((c) => ({
        id: c.id,
        name: c.name,
        quantity: c.quantity,
        organizationId: c.organizationId,
        pricePerUnit: c.pricePerUnit,
        price: c.price,
      })),
      sellerConsumables: recipe.sellerConsumables.map((c) => ({
        id: c.id,
        name: c.name,
        quantity: c.quantity,
        organizationId: c.organizationId,
        pricePerUnit: c.pricePerUnit,
        price: c.price,
      })),
    }
  }

  /**
   * FULFILLMENT видит рецептуру только для своих заказов, но без коммерческих данных селлера
   */
  private static filterForFulfillment(
    recipe: ProductRecipe,
    userOrgId: string,
    fulfillmentId?: string,
  ): FilteredRecipe | null {
    // Фулфилмент видит рецептуру только если он назначен на заказ
    if (fulfillmentId && fulfillmentId !== userOrgId) {
      return null
    }

    return {
      // Услуги фулфилмента - видит свои с ценами
      services: recipe.services
        .filter((s) => s.organizationId === userOrgId)
        .map((s) => ({
          id: s.id,
          name: s.name,
          organizationId: s.organizationId,
          price: s.price,
          pricePerUnit: s.pricePerUnit,
        })),

      // Расходники фулфилмента - видит свои с ценами
      fulfillmentConsumables: recipe.fulfillmentConsumables
        .filter((c) => c.organizationId === userOrgId)
        .map((c) => ({
          id: c.id,
          name: c.name,
          quantity: c.quantity,
          organizationId: c.organizationId,
          pricePerUnit: c.pricePerUnit,
          price: c.price,
        })),

      // Расходники селлера - видит только ID, название и количество (БЕЗ цен)
      sellerConsumables: recipe.sellerConsumables.map((c) => ({
        id: c.id,
        name: c.name,
        quantity: c.quantity,
        // НЕ показываем коммерческие данные селлера
        organizationId: undefined,
        pricePerUnit: undefined,
        price: undefined,
      })),
    }
  }

  /**
   * Проверяет доступ к услугам фулфилмента
   */
  static async validateServiceAccess(
    prisma: PrismaClient,
    serviceIds: string[],
    fulfillmentId: string,
    userOrgId: string,
    userRole: OrganizationType,
  ): Promise<boolean> {
    try {
      // Только селлер и назначенный фулфилмент могут видеть услуги
      if (userRole !== 'SELLER' && userRole !== 'FULFILLMENT') {
        throw new GraphQLError('Access denied to fulfillment services', {
          extensions: { code: 'FORBIDDEN' },
        })
      }

      // Если это фулфилмент, проверяем что услуги принадлежат ему
      if (userRole === 'FULFILLMENT' && userOrgId !== fulfillmentId) {
        throw new GraphQLError('Access denied to services of other fulfillment', {
          extensions: { code: 'FORBIDDEN' },
        })
      }

      const services = await prisma.service.findMany({
        where: {
          id: { in: serviceIds },
          organizationId: fulfillmentId,
        },
        select: {
          id: true,
          organizationId: true,
        },
      })

      if (services.length !== serviceIds.length) {
        const foundIds = services.map((s) => s.id)
        const missingIds = serviceIds.filter((id) => !foundIds.includes(id))

        SecurityLogger.logSecurityError(new Error('Service access violation'), {
          operation: 'validateServiceAccess',
          serviceIds,
          foundIds,
          missingIds,
          fulfillmentId,
          userOrgId,
          userRole,
        })

        throw new GraphQLError('Some services do not belong to this fulfillment center', {
          extensions: {
            code: 'FORBIDDEN',
            missingServices: missingIds,
          },
        })
      }

      return true
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'validateServiceAccess',
        serviceIds,
        fulfillmentId,
        userOrgId,
        userRole,
      })
      throw error
    }
  }

  /**
   * Проверяет доступ к расходникам
   */
  static async validateConsumableAccess(
    prisma: PrismaClient,
    consumableIds: string[],
    organizationId: string,
    userOrgId: string,
    userRole: OrganizationType,
  ): Promise<boolean> {
    try {
      // Доступ к расходникам могут иметь:
      // - Владелец расходников
      // - Селлер (для своих расходников)
      // - Назначенный фулфилмент (для расходников фулфилмента)

      let allowedToAccess = false

      switch (userRole) {
        case 'SELLER':
          // Селлер может видеть свои расходники
          allowedToAccess = userOrgId === organizationId
          break

        case 'FULFILLMENT':
          // Фулфилмент может видеть свои расходники
          allowedToAccess = userOrgId === organizationId
          break

        case 'WHOLESALE':
        case 'LOGIST':
          // Поставщики и логистика не видят расходники
          allowedToAccess = false
          break
      }

      if (!allowedToAccess) {
        throw new GraphQLError('Access denied to consumables', {
          extensions: { code: 'FORBIDDEN' },
        })
      }

      const consumables = await prisma.supply.findMany({
        where: {
          id: { in: consumableIds },
          organizationId: organizationId,
        },
        select: {
          id: true,
          organizationId: true,
        },
      })

      if (consumables.length !== consumableIds.length) {
        throw new GraphQLError('Some consumables do not belong to this organization', {
          extensions: { code: 'FORBIDDEN' },
        })
      }

      return true
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'validateConsumableAccess',
        consumableIds,
        organizationId,
        userOrgId,
        userRole,
      })
      throw error
    }
  }

  /**
   * Получает список удаленных полей для логирования
   */
  private static getRemovedFields(original: ProductRecipe, filtered: FilteredRecipe | null): string[] {
    if (!filtered) {
      return ['services', 'fulfillmentConsumables', 'sellerConsumables']
    }

    const removedFields: string[] = []

    // Проверяем удаленные услуги
    if (filtered.services.length < original.services.length) {
      removedFields.push('services')
    }

    // Проверяем удаленные расходники фулфилмента
    if (filtered.fulfillmentConsumables.length < original.fulfillmentConsumables.length) {
      removedFields.push('fulfillmentConsumables')
    }

    // Проверяем удаленные цены расходников селлера
    const originalSellerConsumables = original.sellerConsumables
    const filteredSellerConsumables = filtered.sellerConsumables

    if (
      filteredSellerConsumables.some(
        (c, i) => c.price === undefined && originalSellerConsumables[i]?.price !== undefined,
      )
    ) {
      removedFields.push('sellerConsumables.price')
    }

    return removedFields
  }
}
