import { logger } from '@goodgo/logger';
import { PrismaClient } from '@prisma/client';

import { getPrismaClient } from '../../config/database.config';

/**
 * EN: Policy context for ABAC evaluation
 * VI: Context chính sách cho đánh giá ABAC
 */
export interface PolicyContext {
  userId: string;
  resource: string;
  action: string;
  scope?: string;
  attributes?: Record<string, any>; // Additional attributes for ABAC
}

/**
 * EN: Policy Engine for Attribute-Based Access Control (ABAC)
 * VI: Policy Engine cho kiểm soát truy cập dựa trên thuộc tính (ABAC)
 */
export class PolicyEngine {
  private prisma: PrismaClient;

  constructor() {
    this.prisma = getPrismaClient();
  }

  /**
   * EN: Evaluate policy for given context
   * VI: Đánh giá chính sách cho context đã cho
   */
  async evaluate(context: PolicyContext): Promise<boolean> {
    try {
      // Load active policies for the resource
      const policies = await this.prisma.policy.findMany({
        where: {
          resource: context.resource,
          isActive: true,
        },
        orderBy: {
          priority: 'desc', // Higher priority first
        },
      });

      if (policies.length === 0) {
        // No policies = allow (default allow)
        return true;
      }

      // Evaluate each policy
      for (const policy of policies) {
        const result = this.evaluatePolicy(policy, context);
        
        if (policy.effect === 'DENY' && result) {
          // Deny policy matched = deny access
          logger.debug('Policy denied access', { policyId: policy.id, context });
          return false;
        }
        
        if (policy.effect === 'ALLOW' && result) {
          // Allow policy matched = allow access
          logger.debug('Policy allowed access', { policyId: policy.id, context });
          return true;
        }
      }

      // Default deny if no policy matched
      return false;
    } catch (error) {
      logger.error('Policy evaluation error', { error, context });
      // Fail secure: deny on error
      return false;
    }
  }

  /**
   * EN: Evaluate single policy condition
   * VI: Đánh giá điều kiện chính sách đơn
   */
  private evaluatePolicy(policy: any, context: PolicyContext): boolean {
    try {
      const condition = policy.condition as any;

      // Simple JSON logic evaluation
      // In production, use a proper JSON logic library like json-logic-js
      return this.evaluateCondition(condition, context);
    } catch (error) {
      logger.error('Policy condition evaluation error', { error, policyId: policy.id });
      return false;
    }
  }

  /**
   * EN: Evaluate condition using JSON logic
   * VI: Đánh giá điều kiện sử dụng JSON logic
   */
  private evaluateCondition(condition: any, context: PolicyContext): boolean {
    if (typeof condition !== 'object' || condition === null) {
      return false;
    }

    // Handle operators
    if (condition.and) {
      return condition.and.every((c: any) => this.evaluateCondition(c, context));
    }

    if (condition.or) {
      return condition.or.some((c: any) => this.evaluateCondition(c, context));
    }

    if (condition.not) {
      return !this.evaluateCondition(condition.not, context);
    }

    // Handle comparisons
    if (condition.operator) {
      const { operator, field, value } = condition;
      const fieldValue = this.getFieldValue(field, context);

      switch (operator) {
        case 'eq':
          return fieldValue === value;
        case 'ne':
          return fieldValue !== value;
        case 'gt':
          return fieldValue > value;
        case 'gte':
          return fieldValue >= value;
        case 'lt':
          return fieldValue < value;
        case 'lte':
          return fieldValue <= value;
        case 'in':
          return Array.isArray(value) && value.includes(fieldValue);
        case 'contains':
          return String(fieldValue).includes(value);
        default:
          return false;
      }
    }

    return false;
  }

  /**
   * EN: Get field value from context
   * VI: Lấy giá trị field từ context
   */
  private getFieldValue(field: string, context: PolicyContext): any {
    // Support dot notation for nested fields
    const parts = field.split('.');
    let value: any = context;

    for (const part of parts) {
      if (value && typeof value === 'object' && part in value) {
        value = value[part];
      } else {
        return undefined;
      }
    }

    return value;
  }

  /**
   * EN: Create policy
   * VI: Tạo chính sách
   */
  async createPolicy(data: {
    name: string;
    description?: string;
    resource: string;
    condition: any;
    effect: 'ALLOW' | 'DENY';
    priority?: number;
  }): Promise<any> {
    return this.prisma.policy.create({
      data: {
        name: data.name,
        description: data.description,
        resource: data.resource,
        condition: data.condition,
        effect: data.effect,
        priority: data.priority || 0,
      },
    });
  }

  /**
   * EN: Update policy
   * VI: Cập nhật chính sách
   */
  async updatePolicy(policyId: string, data: Partial<{
    name: string;
    description: string;
    condition: any;
    effect: 'ALLOW' | 'DENY';
    priority: number;
    isActive: boolean;
  }>): Promise<any> {
    return this.prisma.policy.update({
      where: { id: policyId },
      data,
    });
  }

  /**
   * EN: Delete policy
   * VI: Xóa chính sách
   */
  async deletePolicy(policyId: string): Promise<void> {
    await this.prisma.policy.delete({
      where: { id: policyId },
    });
  }
}

export const policyEngine = new PolicyEngine();
