import { Arbiter } from '../../src/core/Arbiter.js';

/**
 * Big Graph Generator for Enterprise Authorization Testing
 * Generates realistic authorization graphs with complex multi-hop relationships
 */
export class BigGraphGenerator {
  constructor(config = {}) {
    this.config = {
      scale: 'small',
      seed: 12345,
      ...config
    };
    
    // Scale configurations - reduced for testing
    this.scaleConfigs = {
      small: {
        users: 100,
        documents: 500,
        relations: 1000,
        complexChains: 200, // Multi-hop authorization chains
        targetQPS: 100,
        maxMemoryMB: 128,
        maxLatencyMs: 50
      },
      medium: {
        users: 500,
        documents: 2000,
        relations: 5000,
        complexChains: 1000,
        targetQPS: 200,
        maxMemoryMB: 256,
        maxLatencyMs: 100
      },
      large: {
        users: 1000,
        documents: 5000,
        relations: 10000,
        complexChains: 2000,
        targetQPS: 500,
        maxMemoryMB: 512,
        maxLatencyMs: 200
      },
      enterprise: {
        users: 2000,
        documents: 10000,
        relations: 20000,
        complexChains: 5000,
        targetQPS: 1000,
        maxMemoryMB: 1024,
        maxLatencyMs: 500
      },
      million: {
        users: 100000,
        documents: 900000,
        relations: 2000000,
        complexChains: 10000, // Reduced complexity to avoid stack overflow
        targetQPS: 100,
        maxMemoryMB: 8192,
        maxLatencyMs: 1000
      }
    };
    
    // Initialize random number generator with seed
    this.rng = this._createSeededRNG(this.config.seed);
  }

  /**
   * Create seeded random number generator for reproducible results
   */
  _createSeededRNG(seed) {
    let state = seed;
    return {
      next: () => {
        state = (state * 1664525 + 1013904223) % 4294967296;
        return state / 4294967296;
      }
    };
  }

  /**
   * Generate authorization graph for specified model
   */
  generateGraph(model = 'enterprise') {
    const scale = this.scaleConfigs[this.config.scale];
    
    const graphData = {
      users: [],
      documents: [],
      relations: [],
      enterprises: [],
      saasTenants: [],
      metadata: {
        model,
        scale: this.config.scale,
        generatedAt: new Date().toISOString(),
        seed: this.config.seed
      }
    };

    // Generate users
    graphData.users = this._generateUsers(scale);
    
    // Generate documents
    graphData.documents = this._generateDocuments(scale);
    
    // Generate basic user-document relationships
    const basicRelations = this._generateBasicRelations(scale, graphData.users, graphData.documents);
    graphData.relations.push(...basicRelations);
    
    // Generate complex authorization chains (multi-hop relationships)
    const { complexChains, roleNodes, departmentNodes } = this._generateComplexAuthorizationChains(scale, graphData.users, graphData.documents);
    graphData.relations.push(...complexChains);
    
    // Add the generated nodes to the graph data
    graphData.roles = roleNodes;
    graphData.departments = departmentNodes;
    
    // Generate organizational hierarchies
    const orgRelations = this._generateOrganizationalHierarchies(scale, graphData.users);
    graphData.relations.push(...orgRelations);
    
    // Generate long organizational chains for multi-hop testing
    const longChains = this._generateLongOrganizationalChains(scale, graphData.users, graphData.documents);
    graphData.relations.push(...longChains);
    
    // Generate role-based access chains
    const roleChains = this._generateRoleBasedChains(scale, graphData.users, graphData.documents);
    graphData.relations.push(...roleChains);

    return graphData;
  }

  /**
   * Load graph data into Arbiter instance
   */
  loadIntoArbiter(graphData, options = {}) {
    const arbiter = new Arbiter({ 
      fastConstructionMode: true,
      ...options
    });
    
    // Add all nodes
    graphData.users.forEach(user => {
      arbiter.addNode(user.id, 'user');
    });
    
    graphData.documents.forEach(doc => {
      arbiter.addNode(doc.id, 'document');
    });
    
    // Add role nodes if they exist
    if (graphData.roles) {
      graphData.roles.forEach(role => {
        arbiter.addNode(role.id, 'role');
      });
    }
    
    // Add department nodes if they exist
    if (graphData.departments) {
      graphData.departments.forEach(dept => {
        arbiter.addNode(dept.id, 'department');
      });
    }
    
    // Add intermediate nodes for long chains
    const longChainRelations = graphData.relations.filter(r => r.metadata?.type === 'long_chain');
    const intermediateNodes = new Set();
    
    longChainRelations.forEach(relation => {
      // Add all intermediate nodes (teams, departments, divisions, companies)
      if (relation.src.startsWith('team:') || relation.src.startsWith('dept:') || 
          relation.src.startsWith('division:') || relation.src.startsWith('company:')) {
        intermediateNodes.add(relation.src);
      }
      if (relation.dst.startsWith('team:') || relation.dst.startsWith('division:') || relation.dst.startsWith('company:')) {
        intermediateNodes.add(relation.dst);
      }
    });
    
    intermediateNodes.forEach(nodeId => {
      const nodeType = nodeId.startsWith('team:') ? 'team' : 
                     nodeId.startsWith('dept:') ? 'department' :
                     nodeId.startsWith('division:') ? 'division' : 'company';
      arbiter.addNode(nodeId, nodeType);
    });
    
    // Add all relations
    graphData.relations.forEach(relation => {
      arbiter.addRelation(relation.src, relation.relation, relation.dst, relation.possibility);
    });
    
    // Disable fast construction mode and build indices now that loading is complete
    arbiter.setFastConstructionMode(false);
    
    // Configure relation types with proper ReBAC patterns
    const relationTypes = ['can_read', 'can_write', 'can_delete', 'can_share', 'can_admin'];
    relationTypes.forEach(relType => {
      // Configure direct relations
      arbiter.setRelationConfig(relType, { type: 'direct' });
      
      // Configure role-based access using chain rule for proper role-based access
      const roleBasedRel = `${relType}_via_role`;
      arbiter.setRelationConfig(roleBasedRel, {
        type: 'chain',
        steps: [
          { relation: 'member_of', direction: 'out' },
          { relation: relType, direction: 'out' }
        ]
      });
    });
    
    // Configure membership relations as direct
    const membershipTypes = ['member_of', 'manager_of', 'reports_to'];
    membershipTypes.forEach(relType => {
      arbiter.setRelationConfig(relType, { type: 'direct' });
    });
    
    // Configure department-based access using chain rule
    const departmentBasedRel = 'can_write_via_department';
    arbiter.setRelationConfig(departmentBasedRel, {
      type: 'chain',
      steps: [
        { relation: 'member_of', direction: 'out' },
        { relation: 'can_write', direction: 'out' }
      ]
    });
    
    // Configure multi-hop access for long chain traversal (computationally intensive)
    // Use chain rule for complex multi-step authorization
    arbiter.setRelationConfig('can_access_multi_hop', {
      type: 'chain',
      steps: [
        { relation: 'member_of', direction: 'out' },
        { relation: 'member_of', direction: 'out' },
        { relation: 'member_of', direction: 'out' },
        { relation: 'member_of', direction: 'out' },
        { relation: 'can_read', direction: 'out' }
      ],
      collectValues: true,
      valueAggregation: 'sum'
    });
    
    return arbiter;
  }

  /**
   * Generate users
   */
  _generateUsers(scale) {
    const users = [];
    const names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack'];
    const surnames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez'];
    
    for (let i = 0; i < scale.users; i++) {
      const firstName = names[Math.floor(this.rng.next() * names.length)];
      const lastName = surnames[Math.floor(this.rng.next() * surnames.length)];
      
      users.push({
        id: `user:${firstName} ${lastName}-${i}`,
        type: 'employee',
        name: `${firstName} ${lastName}`,
        email: `${firstName.toLowerCase()} ${lastName.toLowerCase()}@company.com`,
        role: ['manager', 'employee', 'contractor', 'intern'][Math.floor(this.rng.next() * 4)],
        clearance: ['public', 'internal', 'confidential', 'secret', 'top_secret'][Math.floor(this.rng.next() * 5)],
        department: `dept:${this._generateDepartmentName()}-${i}`,
        organization: `org:company-${Math.floor(i / 50)}`,
        manager: null,
        startDate: new Date(Date.now() - Math.floor(this.rng.next() * 365 * 24 * 60 * 60 * 1000)).toISOString(),
        isActive: true
      });
    }
    
    return users;
  }

  /**
   * Generate documents
   */
  _generateDocuments(scale) {
    const documents = [];
    const docTypes = ['report', 'analysis', 'proposal', 'contract', 'policy', 'spec', 'budget'];
    
    for (let i = 0; i < scale.documents; i++) {
      const docType = docTypes[Math.floor(this.rng.next() * docTypes.length)];
      
      documents.push({
        id: `doc:${docType}-${i}`,
        type: 'document',
        name: `${docType}_${i}`,
        classification: ['public', 'internal', 'confidential', 'secret', 'top_secret'][Math.floor(this.rng.next() * 5)],
        department: `dept:${this._generateDepartmentName()}-${i}`,
        organization: `org:company-${Math.floor(i / 100)}`,
        requiredClearance: ['public', 'internal', 'confidential', 'secret', 'top_secret'][Math.floor(this.rng.next() * 5)],
        cost: Math.floor(this.rng.next() * 10000),
        createdDate: new Date(Date.now() - Math.floor(this.rng.next() * 365 * 24 * 60 * 60 * 1000)).toISOString(),
        lastModified: new Date(Date.now() - Math.floor(this.rng.next() * 30 * 24 * 60 * 60 * 1000)).toISOString(),
        size: Math.floor(this.rng.next() * 1000000),
        tags: this._generateTags()
      });
    }
    
    return documents;
  }

  /**
   * Generate basic user-document relationships
   * FIXED: Reduce direct relations to avoid conflicts with chain relations
   */
  _generateBasicRelations(scale, users, documents) {
    const relations = [];
    const operations = ['can_read', 'can_write', 'can_delete', 'can_share'];
    
    // Reduce the number of direct relations to avoid conflicts with chain relations
    const reducedRelations = Math.floor(scale.relations * 0.3); // Only 30% of original
    
    for (let i = 0; i < reducedRelations; i++) {
      const user = users[Math.floor(this.rng.next() * users.length)];
      const document = documents[Math.floor(this.rng.next() * documents.length)];
      const operation = operations[Math.floor(this.rng.next() * operations.length)];
      const possibility = 0.5 + (this.rng.next() * 0.5); // 0.5 to 1.0
      
      relations.push({
        src: user.id,
        relation: operation,
        dst: document.id,
        possibility,
        metadata: {
          role: user.role,
          clearance: user.clearance,
          department: user.department,
          grantedDate: new Date().toISOString(),
          expiresDate: null
        }
      });
    }
    
    return relations;
  }

  /**
   * Generate complex authorization chains (multi-hop relationships)
   * These create realistic enterprise authorization scenarios
   */
  _generateComplexAuthorizationChains(scale, users, documents) {
    const relations = [];
    const roleNodes = [];
    const departmentNodes = [];
    const chainCount = scale.complexChains || Math.floor(scale.relations * 0.2);
    
    // Create role nodes first
    const roleTypes = ['admin', 'manager', 'employee', 'contractor', 'intern'];
    const createdRoles = new Set();
    
    for (let i = 0; i < chainCount; i++) {
      const user = users[Math.floor(this.rng.next() * users.length)];
      const document = documents[Math.floor(this.rng.next() * documents.length)];
      
      // Chain 1: User -> Role -> Document (Role-based access)
      const roleType = roleTypes[Math.floor(this.rng.next() * roleTypes.length)];
      const roleId = `role:${roleType}-${Math.floor(this.rng.next() * 10)}`;
      
      // Create role node if it doesn't exist
      if (!createdRoles.has(roleId)) {
        roleNodes.push({
          id: roleId,
          type: 'role',
          name: `${roleType} role`,
          roleType: roleType,
          permissions: ['can_read', 'can_write', 'can_share']
        });
        createdRoles.add(roleId);
      }
      
      relations.push({
        src: user.id,
        relation: 'member_of',
        dst: roleId,
        possibility: 0.9,
        metadata: { type: 'role_membership' }
      });
      
      relations.push({
        src: roleId,
        relation: 'can_read',
        dst: document.id,
        possibility: 0.8,
        metadata: { type: 'role_permission' }
      });
      
      // Chain 2: User -> Department -> Document (Department-based access)
      const deptName = this._generateDepartmentName();
      const deptId = `dept:${deptName}-${Math.floor(this.rng.next() * 20)}`;
      
      // Create department node if it doesn't exist
      if (!createdRoles.has(deptId)) {
        departmentNodes.push({
          id: deptId,
          type: 'department',
          name: `${deptName} department`,
          departmentName: deptName,
          permissions: ['can_read', 'can_write']
        });
        createdRoles.add(deptId);
      }
      
      relations.push({
        src: user.id,
        relation: 'member_of',
        dst: deptId,
        possibility: 0.95,
        metadata: { type: 'department_membership' }
      });
      
      relations.push({
        src: deptId,
        relation: 'can_write',
        dst: document.id,
        possibility: 0.7,
        metadata: { type: 'department_permission' }
      });
      
      // Chain 3: User -> Manager -> Document (Manager approval chain)
      const manager = users[Math.floor(this.rng.next() * users.length)];
      relations.push({
        src: user.id,
        relation: 'reports_to',
        dst: manager.id,
        possibility: 0.9,
        metadata: { type: 'reporting_chain' }
      });
      
      relations.push({
        src: manager.id,
        relation: 'can_share',
        dst: document.id,
        possibility: 0.6,
        metadata: { type: 'manager_permission' }
      });
    }
    
    return { complexChains: relations, roleNodes, departmentNodes };
  }

  /**
   * Generate long organizational chains for multi-hop testing
   * These create deep hierarchies that will be computationally intensive to traverse
   */
  _generateLongOrganizationalChains(scale, users, documents) {
    const relations = [];
    const chainCount = Math.floor(scale.users * 0.1); // 10% of users get long chains
    
    for (let i = 0; i < chainCount; i++) {
      const user = users[Math.floor(this.rng.next() * users.length)];
      const document = documents[Math.floor(this.rng.next() * documents.length)];
      
      // Create a long chain: User -> Team -> Department -> Division -> Company -> Document
      const teamId = `team:${this._generateTeamName()}-${Math.floor(this.rng.next() * 50)}`;
      const deptId = `dept:${this._generateDepartmentName()}-${Math.floor(this.rng.next() * 100)}`;
      const divisionId = `division:${this._generateDivisionName()}-${Math.floor(this.rng.next() * 20)}`;
      const companyId = `company:${this._generateCompanyName()}-${Math.floor(this.rng.next() * 10)}`;
      
      // Create the long chain with decreasing possibility (realistic for deep hierarchies)
      relations.push({
        src: user.id,
        relation: 'member_of',
        dst: teamId,
        possibility: 0.95,
        metadata: { type: 'long_chain', step: 1, chainId: `chain-${i}` }
      });
      
      relations.push({
        src: teamId,
        relation: 'member_of',
        dst: deptId,
        possibility: 0.9,
        metadata: { type: 'long_chain', step: 2, chainId: `chain-${i}` }
      });
      
      relations.push({
        src: deptId,
        relation: 'member_of',
        dst: divisionId,
        possibility: 0.85,
        metadata: { type: 'long_chain', step: 3, chainId: `chain-${i}` }
      });
      
      relations.push({
        src: divisionId,
        relation: 'member_of',
        dst: companyId,
        possibility: 0.8,
        metadata: { type: 'long_chain', step: 4, chainId: `chain-${i}` }
      });
      
      relations.push({
        src: companyId,
        relation: 'can_read',
        dst: document.id,
        possibility: 0.75,
        metadata: { type: 'long_chain', step: 5, chainId: `chain-${i}` }
      });
    }
    
    return relations;
  }

  /**
   * Generate organizational hierarchies
   */
  _generateOrganizationalHierarchies(scale, users) {
    const relations = [];
    
    // Create department hierarchies
    for (let i = 0; i < Math.floor(scale.users / 10); i++) {
      const deptId = `dept:${this._generateDepartmentName()}-${i}`;
      const parentDeptId = `dept:${this._generateDepartmentName()}-${Math.floor(i / 3)}`;
      
      if (i > 0) {
        relations.push({
          src: deptId,
          relation: 'reports_to',
          dst: parentDeptId,
          possibility: 0.9,
          metadata: { type: 'department_hierarchy' }
        });
      }
    }
    
    return relations;
  }

  /**
   * Generate role-based access chains
   */
  _generateRoleBasedChains(scale, users, documents) {
    const relations = [];
    
    // Create role hierarchies
    const roles = ['admin', 'manager', 'senior', 'employee', 'contractor', 'intern'];
    
    for (let i = 0; i < roles.length - 1; i++) {
      const currentRole = `role:${roles[i]}`;
      const parentRole = `role:${roles[i + 1]}`;
      
      relations.push({
        src: currentRole,
        relation: 'inherits_from',
        dst: parentRole,
        possibility: 0.8,
        metadata: { type: 'role_inheritance' }
      });
    }
    
    return relations;
  }

  /**
   * Generate department name
   */
  _generateDepartmentName() {
    const departments = ['engineering', 'finance', 'hr', 'legal', 'operations', 'marketing', 'sales'];
    return departments[Math.floor(this.rng.next() * departments.length)];
  }

  _generateTeamName() {
    const teams = ['Frontend', 'Backend', 'DevOps', 'QA', 'Design', 'Analytics', 'Security', 'Mobile'];
    return teams[Math.floor(this.rng.next() * teams.length)];
  }

  _generateDivisionName() {
    const divisions = ['Product', 'Engineering', 'Sales', 'Marketing', 'Operations', 'Finance', 'Legal', 'HR'];
    return divisions[Math.floor(this.rng.next() * divisions.length)];
  }

  _generateCompanyName() {
    const companies = ['AcmeCorp', 'TechGiant', 'InnovateLabs', 'DataFlow', 'CloudSystems', 'NextGen', 'FutureTech', 'SmartSolutions'];
    return companies[Math.floor(this.rng.next() * companies.length)];
  }

  /**
   * Generate document tags
   */
  _generateTags() {
    const allTags = ['important', 'urgent', 'confidential', 'draft', 'final', 'reviewed', 'approved'];
    const tagCount = Math.floor(this.rng.next() * 3) + 1;
    const tags = [];
    
    for (let i = 0; i < tagCount; i++) {
      const tag = allTags[Math.floor(this.rng.next() * allTags.length)];
      if (!tags.includes(tag)) {
        tags.push(tag);
      }
    }
    
    return tags;
  }
}