import path from 'path';
import { ConfigManager } from '../../config/config-manager.js';
import { 
  ProcessDefinition, 
  ProcessExecution,
  ProcessTemplate
} from './types.js';
import { FileSystemAdapter, NodeFileSystemAdapter } from './file-system-adapter.js';

export class ProcessStore {
  private dataPath: string = '';
  private configManager: ConfigManager;
  private processCache: Map<string, ProcessDefinition> = new Map();
  private executionCache: Map<string, ProcessExecution> = new Map();
  private fs: FileSystemAdapter;

  constructor(configManager: ConfigManager, fs?: FileSystemAdapter) {
    this.configManager = configManager;
    this.fs = fs || new NodeFileSystemAdapter();
  }

  async init(): Promise<void> {
    const storageManager = this.configManager.getStorageManager();
    this.dataPath = await storageManager.getModuleDataPath('process-automation', '');
    
    // Ensure subdirectories exist
    await this.fs.mkdir(path.join(this.dataPath, 'processes'), { recursive: true });
    await this.fs.mkdir(path.join(this.dataPath, 'executions'), { recursive: true });
    await this.fs.mkdir(path.join(this.dataPath, 'templates'), { recursive: true });
  }

  // Process Definition Methods
  async saveProcess(process: ProcessDefinition): Promise<void> {
    const filePath = path.join(this.dataPath, 'processes', `${process.id}.json`);
    await this.fs.writeFile(filePath, JSON.stringify(process, null, 2));
    this.processCache.set(process.id, process);
  }

  async getProcess(processId: string): Promise<ProcessDefinition | null> {
    // Check cache first
    if (this.processCache.has(processId)) {
      return this.processCache.get(processId)!;
    }
    
    try {
      const filePath = path.join(this.dataPath, 'processes', `${processId}.json`);
      await this.fs.access(filePath);
      const data = await this.fs.readFile(filePath, 'utf-8');
      const process = JSON.parse(data);
      this.processCache.set(processId, process);
      return process;
    } catch (error) {
      return null;
    }
  }

  async getAllProcesses(filters?: {
    persona?: string;
    hasEnabledTriggers?: boolean;
  }): Promise<ProcessDefinition[]> {
    try {
      const files = await this.fs.readdir(path.join(this.dataPath, 'processes'));
      const processes: ProcessDefinition[] = [];
      
      for (const file of files) {
        if (file.endsWith('.json')) {
          const data = await this.fs.readFile(
            path.join(this.dataPath, 'processes', file),
            'utf-8'
          );
          const process = JSON.parse(data) as ProcessDefinition;
          
          // Apply filters
          if (filters?.persona && process.persona !== filters.persona) {
            continue;
          }
          
          if (filters?.hasEnabledTriggers) {
            const hasEnabled = process.triggers.some(t => t.enabled);
            if (!hasEnabled) {
              continue;
            }
          }
          
          processes.push(process);
        }
      }
      
      return processes;
    } catch (error) {
      return [];
    }
  }

  async deleteProcess(processId: string): Promise<void> {
    const filePath = path.join(this.dataPath, 'processes', `${processId}.json`);
    await this.fs.unlink(filePath).catch(() => {}); // Ignore if doesn't exist
    this.processCache.delete(processId);
  }

  // Execution Methods
  async saveExecution(execution: ProcessExecution): Promise<void> {
    // Ensure the process-specific execution directory exists
    const processDir = path.join(this.dataPath, 'executions', execution.processId);
    await this.fs.mkdir(processDir, { recursive: true });
    
    const filePath = path.join(processDir, `${execution.id}.json`);
    await this.fs.writeFile(filePath, JSON.stringify(execution, null, 2));
  }

  async getExecution(processId: string, executionId: string): Promise<ProcessExecution | null> {
    try {
      const filePath = path.join(this.dataPath, 'executions', processId, `${executionId}.json`);
      const data = await this.fs.readFile(filePath, 'utf-8');
      return JSON.parse(data);
    } catch (error) {
      return null;
    }
  }

  async getExecutionsForProcess(processId: string): Promise<ProcessExecution[]> {
    try {
      const processDir = path.join(this.dataPath, 'executions', processId);
      const files = await this.fs.readdir(processDir);
      const executions: ProcessExecution[] = [];
      
      for (const file of files) {
        if (file.endsWith('.json')) {
          const data = await this.fs.readFile(
            path.join(processDir, file),
            'utf-8'
          );
          const execution = JSON.parse(data) as ProcessExecution;
          executions.push(execution);
        }
      }
      
      return executions.sort((a, b) => 
        new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
      );
    } catch (error) {
      return [];
    }
  }

  async getRecentExecutions(limit: number = 10): Promise<ProcessExecution[]> {
    try {
      const executionsDirs = await this.fs.readdir(path.join(this.dataPath, 'executions'));
      const executions: ProcessExecution[] = [];
      
      for (const processDir of executionsDirs) {
        try {
          const files = await this.fs.readdir(path.join(this.dataPath, 'executions', processDir));
          for (const file of files) {
            if (file.endsWith('.json')) {
              const data = await this.fs.readFile(
                path.join(this.dataPath, 'executions', processDir, file),
                'utf-8'
              );
              executions.push(JSON.parse(data));
            }
          }
        } catch {
          // Skip if not a directory
        }
      }
      
      return executions
        .sort((a, b) => 
          new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
        )
        .slice(0, limit);
    } catch (error) {
      return [];
    }
  }

  // Template Methods
  async saveTemplate(template: ProcessTemplate): Promise<void> {
    const filePath = path.join(this.dataPath, 'templates', `${template.id}.json`);
    await this.fs.writeFile(filePath, JSON.stringify(template, null, 2));
  }

  async getTemplate(templateId: string): Promise<ProcessTemplate | null> {
    try {
      const filePath = path.join(this.dataPath, 'templates', `${templateId}.json`);
      const data = await this.fs.readFile(filePath, 'utf-8');
      return JSON.parse(data);
    } catch (error) {
      return null;
    }
  }

  async getTemplatesByPersona(persona: string): Promise<ProcessTemplate[]> {
    const allFiles = await this.fs.readdir(path.join(this.dataPath, 'templates'));
    const templates: ProcessTemplate[] = [];
    
    for (const file of allFiles) {
      if (file.endsWith('.json')) {
        const data = await this.fs.readFile(
          path.join(this.dataPath, 'templates', file),
          'utf-8'
        );
        const template = JSON.parse(data) as ProcessTemplate;
        
        if (template.persona === persona) {
          templates.push(template);
        }
      }
    }
    
    return templates;
  }

  // Metrics Methods
  async getProcessMetrics(processId: string): Promise<ProcessMetrics> {
    const executions = await this.getExecutionsForProcess(processId);
    
    const completed = executions.filter(e => e.status === 'completed');
    const failed = executions.filter(e => e.status === 'failed');
    
    const totalDuration = completed.reduce((sum, e) => sum + (e.duration || 0), 0);
    const averageDuration = completed.length > 0 ? totalDuration / completed.length : 0;
    
    return {
      executionCount: executions.length,
      successCount: completed.length,
      failureCount: failed.length,
      successRate: executions.length > 0 ? completed.length / executions.length : 0,
      averageDuration,
      lastExecutedAt: executions[0]?.startedAt
    };
  }

  // Search and Filter Methods
  async getProcessExecutions(processId: string, options?: {
    status?: string;
    limit?: number;
  }): Promise<ProcessExecution[]> {
    let executions = await this.getExecutionsForProcess(processId);
    
    if (options?.status) {
      executions = executions.filter(e => e.status === options.status);
    }
    
    if (options?.limit) {
      executions = executions.slice(0, options.limit);
    }
    
    return executions;
  }

  async searchProcesses(query: string): Promise<ProcessDefinition[]> {
    const allProcesses = await this.getAllProcesses();
    const lowerQuery = query.toLowerCase();
    
    return allProcesses.filter(process => 
      process.name.toLowerCase().includes(lowerQuery) ||
      (process.description && process.description.toLowerCase().includes(lowerQuery))
    );
  }

  async getStats(): Promise<{
    totalProcesses: number;
    byPersona: Record<string, number>;
    byTriggerType: Record<string, number>;
    enabledTriggers: number;
    totalTriggers: number;
  }> {
    const processes = await this.getAllProcesses();
    
    const byPersona: Record<string, number> = {};
    const byTriggerType: Record<string, number> = {};
    let enabledTriggers = 0;
    let totalTriggers = 0;
    
    for (const process of processes) {
      // Count by persona
      const persona = process.persona || 'general';
      byPersona[persona] = (byPersona[persona] || 0) + 1;
      
      // Count triggers
      for (const trigger of process.triggers) {
        totalTriggers++;
        if (trigger.enabled) {
          enabledTriggers++;
        }
        byTriggerType[trigger.type] = (byTriggerType[trigger.type] || 0) + 1;
      }
    }
    
    return {
      totalProcesses: processes.length,
      byPersona,
      byTriggerType,
      enabledTriggers,
      totalTriggers
    };
  }

  // Cleanup Methods
  async cleanupOldExecutions(daysToKeep: number = 30): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
    
    try {
      const processDirs = await this.fs.readdir(path.join(this.dataPath, 'executions'));
      let deletedCount = 0;
      
      for (const processDir of processDirs) {
        try {
          const files = await this.fs.readdir(path.join(this.dataPath, 'executions', processDir));
          for (const file of files) {
            if (file.endsWith('.json')) {
              const filePath = path.join(this.dataPath, 'executions', processDir, file);
              // For simplicity, delete all files in cleanup
              await this.fs.unlink(filePath);
              deletedCount++;
            }
          }
        } catch {
          // Skip if not a directory
        }
      }
      
      return deletedCount;
    } catch (error) {
      return 0;
    }
  }
}

interface ProcessMetrics {
  executionCount: number;
  successCount: number;
  failureCount: number;
  successRate: number;
  averageDuration: number;
  lastExecutedAt?: string;
}