import { randomUUID } from 'crypto';
import { EventEmitter } from 'events';
import { 
  ApprovalRequest, 
  ToolPolicy, 
  ActionPolicy,
  ApprovalStore,
  AuditLogEntry,
  HIGH_RISK_OPERATIONS,
  PolicyCondition
} from './types.js';
import { ConfigManager } from '../../config/config-manager.js';

export class ApprovalManager extends EventEmitter {
  private store: ApprovalStore;
  private configManager: ConfigManager;
  private cleanupInterval?: NodeJS.Timeout;

  constructor(configManager?: ConfigManager) {
    super();
    this.configManager = configManager || new ConfigManager();
    this.store = {
      requests: {},
      policies: {},
      auditLog: []
    };
  }

  async init(): Promise<void> {
    await this.loadStore();
    await this.loadDefaultPolicies();
    this.startCleanupTask();
  }

  async createApprovalRequest(params: {
    toolName: string;
    action: string;
    parameters: any;
    userId: string;
    ttl?: number;
  }): Promise<ApprovalRequest> {
    const policy = this.getActionPolicy(params.toolName, params.action);
    
    // Check if approval is required
    if (!policy.requiresApproval) {
      // Check auto-approve conditions
      if (policy.autoApprove?.enabled && this.checkAutoApproveConditions(policy.autoApprove.conditions, params)) {
        await this.logAudit({
          id: randomUUID(),
          timestamp: new Date(),
          userId: params.userId,
          action: params.action,
          toolName: params.toolName,
          parameters: params.parameters,
          result: 'auto_approved',
          riskLevel: policy.riskLevel
        });
        
        this.emit('auto-approved', { toolName: params.toolName, action: params.action });
        return null as any; // Signal that no approval needed
      }
    }

    const ttl = params.ttl || this.getDefaultTTL(policy.riskLevel);
    const request: ApprovalRequest = {
      id: randomUUID(),
      toolName: params.toolName,
      action: params.action,
      parameters: params.parameters,
      riskLevel: policy.riskLevel,
      impact: this.getImpact(params.toolName, params.action),
      reversible: this.isReversible(params.toolName, params.action),
      requestedAt: new Date(),
      requestedBy: params.userId,
      status: 'pending',
      ttl,
      expiresAt: new Date(Date.now() + ttl * 1000)
    };

    this.store.requests[request.id] = request;
    await this.saveStore();
    
    this.emit('approval-requested', request);
    
    return request;
  }

  async approveRequest(requestId: string, userId: string): Promise<boolean> {
    const request = this.store.requests[requestId];
    if (!request || request.status !== 'pending') {
      return false;
    }

    if (new Date() > request.expiresAt) {
      request.status = 'expired';
      await this.saveStore();
      return false;
    }

    request.status = 'approved';
    request.approvedBy = userId;
    request.approvedAt = new Date();

    await this.logAudit({
      id: randomUUID(),
      timestamp: new Date(),
      userId,
      action: request.action,
      toolName: request.toolName,
      parameters: request.parameters,
      result: 'approved',
      riskLevel: request.riskLevel,
      metadata: { requestId }
    });

    await this.saveStore();
    this.emit('approval-granted', request);
    
    return true;
  }

  async rejectRequest(requestId: string, userId: string, reason?: string): Promise<boolean> {
    const request = this.store.requests[requestId];
    if (!request || request.status !== 'pending') {
      return false;
    }

    request.status = 'rejected';
    request.rejectedBy = userId;
    request.rejectedAt = new Date();
    request.rejectionReason = reason;

    await this.logAudit({
      id: randomUUID(),
      timestamp: new Date(),
      userId,
      action: request.action,
      toolName: request.toolName,
      parameters: request.parameters,
      result: 'rejected',
      riskLevel: request.riskLevel,
      metadata: { requestId, reason }
    });

    await this.saveStore();
    this.emit('approval-rejected', request);
    
    return true;
  }

  async getPendingRequests(userId?: string): Promise<ApprovalRequest[]> {
    const now = new Date();
    return Object.values(this.store.requests)
      .filter(r => {
        if (r.status !== 'pending') return false;
        if (r.expiresAt < now) {
          r.status = 'expired';
          return false;
        }
        if (userId && r.requestedBy === userId) return false; // Can't approve own requests
        return true;
      });
  }

  getActionPolicy(toolName: string, action: string): ActionPolicy {
    const toolPolicy = this.store.policies[toolName];
    
    if (toolPolicy?.actions[action]) {
      return toolPolicy.actions[action];
    }

    // Check high-risk operations
    const highRiskOp = HIGH_RISK_OPERATIONS[toolName]?.[action];
    if (highRiskOp) {
      return {
        action,
        requiresApproval: true,
        approvalLevel: highRiskOp.riskLevel === 'critical' ? 'admin' : 'user',
        riskLevel: highRiskOp.riskLevel,
        auditLog: true
      };
    }

    // Default policy
    return {
      action,
      requiresApproval: false,
      approvalLevel: 'user',
      riskLevel: 'low',
      auditLog: false
    };
  }

  private getImpact(toolName: string, action: string): string[] {
    return HIGH_RISK_OPERATIONS[toolName]?.[action]?.impact || ['unknown'];
  }

  private isReversible(toolName: string, action: string): boolean {
    const impact = this.getImpact(toolName, action);
    return !impact.includes('irreversible');
  }

  private getDefaultTTL(riskLevel: string): number {
    const ttlMap = {
      low: 3600, // 1 hour
      medium: 1800, // 30 minutes
      high: 900, // 15 minutes
      critical: 300 // 5 minutes
    };
    return ttlMap[riskLevel] || 1800;
  }

  private checkAutoApproveConditions(conditions: PolicyCondition[], params: any): boolean {
    return conditions.every(condition => {
      const value = this.getNestedValue(params, condition.field);
      return this.evaluateCondition(value, condition.operator, condition.value);
    });
  }

  private getNestedValue(obj: any, path: string): any {
    return path.split('.').reduce((curr, prop) => curr?.[prop], obj);
  }

  private evaluateCondition(value: any, operator: string, expected: any): boolean {
    switch (operator) {
      case 'equals': return value === expected;
      case 'contains': return String(value).includes(expected);
      case 'greater_than': return Number(value) > Number(expected);
      case 'less_than': return Number(value) < Number(expected);
      case 'in': return Array.isArray(expected) && expected.includes(value);
      case 'not_in': return Array.isArray(expected) && !expected.includes(value);
      default: return false;
    }
  }

  private async logAudit(entry: AuditLogEntry): Promise<void> {
    this.store.auditLog.push(entry);
    
    // Keep only last 1000 entries
    if (this.store.auditLog.length > 1000) {
      this.store.auditLog = this.store.auditLog.slice(-1000);
    }
    
    await this.saveStore();
  }

  private startCleanupTask(): void {
    this.cleanupInterval = setInterval(async () => {
      const now = new Date();
      let hasChanges = false;
      
      for (const request of Object.values(this.store.requests)) {
        if (request.status === 'pending' && request.expiresAt < now) {
          request.status = 'expired';
          hasChanges = true;
          this.emit('approval-expired', request);
        }
      }
      
      if (hasChanges) {
        await this.saveStore();
      }
    }, 60000); // Check every minute
  }

  private async loadStore(): Promise<void> {
    const storageManager = this.configManager.getStorageManager();
    const data = await storageManager.loadData('approval-system', 'approval-store.json');
    if (data) {
      this.store = data;
    }
  }

  private async saveStore(): Promise<void> {
    const storageManager = this.configManager.getStorageManager();
    await storageManager.saveData('approval-system', 'approval-store.json', this.store);
  }

  private async loadDefaultPolicies(): Promise<void> {
    // Load default policies from configuration
    const policies = await this.configManager.getStorageManager()
      .loadData('approval-system', 'policies.json');
    
    if (policies) {
      this.store.policies = { ...this.store.policies, ...policies };
    }
  }

  async getAuditLog(filters?: {
    startDate?: Date;
    endDate?: Date;
    userId?: string;
    toolName?: string;
    result?: string;
  }): Promise<AuditLogEntry[]> {
    let logs = [...this.store.auditLog];
    
    if (filters) {
      if (filters.startDate) {
        logs = logs.filter(l => l.timestamp >= filters.startDate!);
      }
      if (filters.endDate) {
        logs = logs.filter(l => l.timestamp <= filters.endDate!);
      }
      if (filters.userId) {
        logs = logs.filter(l => l.userId === filters.userId);
      }
      if (filters.toolName) {
        logs = logs.filter(l => l.toolName === filters.toolName);
      }
      if (filters.result) {
        logs = logs.filter(l => l.result === filters.result);
      }
    }
    
    return logs.reverse(); // Most recent first
  }

  dispose(): void {
    if (this.cleanupInterval) {
      clearInterval(this.cleanupInterval);
    }
  }
}