import { StorageManager } from '../../storage/storage-manager.js';
import { ConfigManager } from '../../config/config-manager.js';
import crypto from 'crypto';
import {
  TestResult,
  TestHistory,
  TestComparison,
  CoverageReport,
  FlakyTest,
  TestBaseline,
} from './types.js';

export class TestingStore {
  private storageManager: StorageManager;
  private moduleName = 'testing-framework';

  constructor(configManager: ConfigManager) {
    this.storageManager = new StorageManager();
  }

  async initialize(): Promise<void> {
    await this.storageManager.ensureStorageDirectories();
  }

  // Test Result Management

  async saveTestResult(result: TestResult): Promise<void> {
    let history: TestResult[] = [];
    try {
      history = await this.storageManager.loadData(this.moduleName, 'test-history.json') || [];
    } catch {
      // File doesn't exist yet
    }

    history.push(result);

    // Keep only last 100 test runs
    if (history.length > 100) {
      history = history.slice(-100);
    }

    await this.storageManager.saveData(this.moduleName, 'test-history.json', history);
  }

  async getTestHistory(limit?: number): Promise<TestResult[]> {
    try {
      const history = await this.storageManager.loadData(this.moduleName, 'test-history.json') as TestResult[];
      
      if (limit && limit > 0) {
        return (history || []).slice(-limit);
      }
      
      return history || [];
    } catch {
      return [];
    }
  }

  async getLatestTestResult(): Promise<TestResult | null> {
    const history = await this.getTestHistory(1);
    return history.length > 0 ? history[0] : null;
  }

  // Coverage Management

  async saveCoverageData(coverage: any): Promise<void> {
    const filename = `coverage-${new Date().toISOString().split('T')[0]}.json`;
    await this.storageManager.saveData(this.moduleName, filename, coverage);

    // Also update latest coverage
    await this.storageManager.saveData(this.moduleName, 'coverage-latest.json', coverage);
  }

  async getLatestCoverage(): Promise<any | null> {
    try {
      return await this.storageManager.loadData(this.moduleName, 'coverage-latest.json');
    } catch {
      return null;
    }
  }

  async getCoverageHistory(days: number = 30): Promise<any[]> {
    const history: any[] = [];
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);

    // This is simplified - in a real implementation, you'd scan for coverage files
    // For now, just return the latest coverage if it exists
    const latest = await this.getLatestCoverage();
    if (latest) {
      history.push(latest);
    }

    return history;
  }

  // Flaky Test Detection

  async trackFlakyTest(testName: string, suiteName: string, passed: boolean): Promise<void> {
    let flakyTests: Record<string, FlakyTest> = {};
    
    try {
      flakyTests = await this.storageManager.loadData(this.moduleName, 'flaky-tests.json') || {};
    } catch {
      // File doesn't exist yet
    }

    const key = `${suiteName}::${testName}`;
    
    if (!flakyTests[key]) {
      flakyTests[key] = {
        testId: crypto.randomUUID(),
        name: testName,
        suite: suiteName,
        failureRate: 0,
        recentResults: [],
        lastFailed: new Date().toISOString(),
        errorPatterns: [],
        testName,
        suiteName,
        firstSeen: new Date().toISOString(),
        lastSeen: new Date().toISOString(),
        failures: 0,
        passes: 0,
        totalRuns: 0,
        recentRuns: [],
      };
    }

    const test = flakyTests[key];
    test.lastSeen = new Date().toISOString();
    test.totalRuns++;
    
    if (passed) {
      test.passes++;
    } else {
      test.failures++;
    }
    
    test.failureRate = test.failures / test.totalRuns;
    
    // Track recent runs (last 10)
    test.recentRuns.push({
      passed,
      timestamp: new Date().toISOString(),
    });
    
    if (test.recentRuns.length > 10) {
      test.recentRuns = test.recentRuns.slice(-10);
    }

    await this.storageManager.saveData(this.moduleName, 'flaky-tests.json', flakyTests);
  }

  async getFlakyTests(threshold: number = 0.1): Promise<FlakyTest[]> {
    try {
      const flakyTests = await this.storageManager.loadData(this.moduleName, 'flaky-tests.json') as Record<string, FlakyTest>;
      
      return Object.values(flakyTests || {})
        .filter(test => 
          test.failureRate > threshold && 
          test.failureRate < (1 - threshold) && 
          test.totalRuns >= 5
        )
        .sort((a, b) => b.failureRate - a.failureRate);
    } catch {
      return [];
    }
  }

  // Test Baselines

  async setTestBaseline(name: string, baseline: TestBaseline): Promise<void> {
    let baselines: Record<string, TestBaseline> = {};
    
    try {
      baselines = await this.storageManager.loadData(this.moduleName, 'test-baselines.json') || {};
    } catch {
      // File doesn't exist yet
    }

    baselines[name] = baseline;

    await this.storageManager.saveData(this.moduleName, 'test-baselines.json', baselines);
  }

  async getTestBaseline(name: string): Promise<TestBaseline | null> {
    try {
      const baselines = await this.storageManager.loadData(this.moduleName, 'test-baselines.json') as Record<string, TestBaseline>;
      return baselines[name] || null;
    } catch {
      return null;
    }
  }

  async getAllBaselines(): Promise<Record<string, TestBaseline>> {
    try {
      return await this.storageManager.loadData(this.moduleName, 'test-baselines.json') || {};
    } catch {
      return {};
    }
  }

  // Additional methods for testing-framework module

  async getTestHistoryForProject(projectId: string): Promise<TestHistory> {
    const history = await this.getTestHistory();
    const projectHistory = history.filter(r => r.projectId === projectId);
    
    // Calculate trends
    const trends = [];
    if (projectHistory.length >= 2) {
      const recent = projectHistory.slice(-10);
      const successRates = recent.map(r => r.summary.successRate);
      const avgSuccessRate = successRates.reduce((a, b) => a + b, 0) / successRates.length;
      const firstRate = successRates[0];
      const lastRate = successRates[successRates.length - 1];
      
      trends.push({
        metric: 'successRate' as const,
        values: successRates.map((v, i) => ({ timestamp: recent[i].timestamp, value: v })),
        trend: lastRate > firstRate ? 'improving' as const : lastRate < firstRate ? 'degrading' as const : 'stable' as const,
        changePercentage: ((lastRate - firstRate) / firstRate) * 100,
      });
    }
    
    return {
      projectId,
      results: projectHistory,
      baseline: projectHistory.find(r => r.tags?.includes('baseline')),
      trends,
      lastUpdated: new Date().toISOString(),
    };
  }

  async compareTestResults(baselineId: string, currentId: string): Promise<TestComparison> {
    const history = await this.getTestHistory();
    const baseline = history.find(r => r.id === baselineId);
    const current = history.find(r => r.id === currentId);
    
    if (!baseline || !current) {
      throw new Error('Test results not found');
    }
    
    const baselineTests = new Set(baseline.suites.flatMap(s => s.tests.map(t => `${s.name}::${t.name}`)));
    const currentTests = new Set(current.suites.flatMap(s => s.tests.map(t => `${s.name}::${t.name}`)));
    
    return {
      baseline,
      current,
      improvements: [],
      regressions: [],
      newTests: Array.from(currentTests).filter(t => !baselineTests.has(t)),
      removedTests: Array.from(baselineTests).filter(t => !currentTests.has(t)),
      coverageChange: {
        lines: (current.coverage?.lines.percentage || 0) - (baseline.coverage?.lines.percentage || 0),
        statements: (current.coverage?.statements.percentage || 0) - (baseline.coverage?.statements.percentage || 0),
        functions: (current.coverage?.functions.percentage || 0) - (baseline.coverage?.functions.percentage || 0),
        branches: (current.coverage?.branches.percentage || 0) - (baseline.coverage?.branches.percentage || 0),
      },
    };
  }

  async saveFlakyTests(tests: FlakyTest[]): Promise<void> {
    await this.storageManager.saveData(this.moduleName, 'flaky-tests-report.json', {
      flakyTests: tests,
      timestamp: new Date().toISOString(),
    });
  }

  async setBaseline(resultId: string, projectId: string): Promise<void> {
    const history = await this.getTestHistory();
    const result = history.find(r => r.id === resultId);
    
    if (!result) {
      throw new Error('Test result not found');
    }
    
    // Add baseline tag
    if (!result.tags) {
      result.tags = [];
    }
    if (!result.tags.includes('baseline')) {
      result.tags.push('baseline');
    }
    
    // Update history
    await this.storageManager.saveData(this.moduleName, 'test-history.json', history);
  }

  async getTestResultById(id: string): Promise<TestResult | null> {
    const history = await this.getTestHistory();
    return history.find(r => r.id === id) || null;
  }

  // Test History Analysis

  async getTestTrend(testName: string, days: number = 7): Promise<Array<{
    timestamp: string;
    passed: boolean;
    duration?: number;
  }>> {
    const history = await this.getTestHistory();
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);

    const trend: Array<{ timestamp: string; passed: boolean; duration?: number }> = [];

    for (const result of history) {
      if (new Date(result.timestamp) < startDate) continue;
      
      const suite = result.suites.find(s => 
        s.tests.some(t => t.name === testName)
      );
      
      if (suite) {
        const test = suite.tests.find(t => t.name === testName);
        if (test) {
          trend.push({
            timestamp: result.timestamp,
            passed: test.status === 'passed',
            duration: test.duration,
          });
        }
      }
    }

    return trend;
  }

  async getSuccessRate(days: number = 30): Promise<number> {
    const history = await this.getTestHistory();
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);

    const recentRuns = history.filter(result => 
      new Date(result.timestamp) >= startDate
    );

    if (recentRuns.length === 0) return 0;

    const successfulRuns = recentRuns.filter(result => 
      result.summary.failed === 0
    ).length;

    return (successfulRuns / recentRuns.length) * 100;
  }

  // Export/Import

  async exportTestData(): Promise<any> {
    const [
      history,
      coverage,
      flakyTests,
      baselines,
    ] = await Promise.all([
      this.getTestHistory(),
      this.getLatestCoverage(),
      this.storageManager.loadData(this.moduleName, 'flaky-tests.json').catch(() => ({})),
      this.getAllBaselines(),
    ]);

    return {
      testHistory: history,
      latestCoverage: coverage,
      flakyTests,
      testBaselines: baselines,
      exportedAt: new Date().toISOString(),
    };
  }

  async importTestData(data: any): Promise<void> {
    if (data.testHistory) {
      await this.storageManager.saveData(this.moduleName, 'test-history.json', data.testHistory);
    }

    if (data.latestCoverage) {
      await this.storageManager.saveData(this.moduleName, 'coverage-latest.json', data.latestCoverage);
    }

    if (data.flakyTests) {
      await this.storageManager.saveData(this.moduleName, 'flaky-tests.json', data.flakyTests);
    }

    if (data.testBaselines) {
      await this.storageManager.saveData(this.moduleName, 'test-baselines.json', data.testBaselines);
    }
  }

  // Cleanup

  async cleanupOldData(daysToKeep: number = 30): Promise<number> {
    let cleanedItems = 0;

    // Clean test history
    const history = await this.getTestHistory();
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);

    const filteredHistory = history.filter(result => 
      new Date(result.timestamp) > cutoffDate
    );

    if (filteredHistory.length < history.length) {
      await this.storageManager.saveData(this.moduleName, 'test-history.json', filteredHistory);
      cleanedItems += history.length - filteredHistory.length;
    }

    // Clean flaky test data that hasn't been seen recently
    const flakyTests = await this.storageManager.loadData(this.moduleName, 'flaky-tests.json').catch(() => ({})) as Record<string, FlakyTest>;
    const filteredFlakyTests: Record<string, FlakyTest> = {};

    for (const [key, test] of Object.entries(flakyTests)) {
      if (new Date(test.lastSeen) > cutoffDate) {
        filteredFlakyTests[key] = test;
      } else {
        cleanedItems++;
      }
    }

    await this.storageManager.saveData(this.moduleName, 'flaky-tests.json', filteredFlakyTests);

    return cleanedItems;
  }
}