/**
 * Integration Test: Complete VPS Flow
 * Tests the entire flow from download request to serving images
 *
 * Flow:
 * 1. Start the server
 * 2. Request download of chapter 789 (one-piece_1695365223767)
 * 3. Wait for download to complete
 * 4. Verify chapter is in storage
 * 5. Request image from storage endpoint
 * 6. Verify image is served correctly
 * 7. Check storage stats
 * 8. Delete chapter
 * 9. Verify it's removed
 */

import assert from 'assert';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import storage from './storage.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// Test configuration
const TEST_CONFIG = {
  mangaSlug: 'one-piece_1695365223767',
  chapterNumber: 789,
  baseUrl: 'http://localhost:3001',
  timeout: 120000 // 2 minutes
};

// Color codes for terminal output
const colors = {
  reset: '\x1b[0m',
  bright: '\x1b[1m',
  green: '\x1b[32m',
  red: '\x1b[31m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  cyan: '\x1b[36m'
};

function log(message, color = 'reset') {
  console.log(`${colors[color]}${message}${colors.reset}`);
}

function logSection(title) {
  console.log('\n' + '='.repeat(70));
  log(title, 'bright');
  console.log('='.repeat(70));
}

function logTest(testName) {
  log(`\n▶ ${testName}`, 'cyan');
}

function logSuccess(message) {
  log(`✓ ${message}`, 'green');
}

function logError(message) {
  log(`✗ ${message}`, 'red');
}

function logInfo(message) {
  log(`  ℹ ${message}`, 'blue');
}

// Test assertions with detailed logging
function assertTruthy(value, message) {
  if (!value) {
    logError(`ASSERTION FAILED: ${message}`);
    throw new Error(message);
  }
  logSuccess(message);
}

function assertEqual(actual, expected, message) {
  if (actual !== expected) {
    logError(`ASSERTION FAILED: ${message}\n  Expected: ${expected}\n  Actual: ${actual}`);
    throw new Error(`${message}: expected ${expected}, got ${actual}`);
  }
  logSuccess(message);
}

// HTTP helpers
async function fetchWithTimeout(url, options = {}, timeout = TEST_CONFIG.timeout) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    clearTimeout(id);
    return response;
  } catch (error) {
    clearTimeout(id);
    throw error;
  }
}

async function waitForCondition(condition, timeoutMs = 30000, intervalMs = 500) {
  const startTime = Date.now();

  while (Date.now() - startTime < timeoutMs) {
    if (await condition()) {
      return true;
    }
    await new Promise(resolve => setTimeout(resolve, intervalMs));
  }

  throw new Error(`Condition not met within ${timeoutMs}ms`);
}

// Storage helpers
function getTestChapterDir() {
  return path.join(__dirname, '../storage/manga', TEST_CONFIG.mangaSlug, `chapter_${TEST_CONFIG.chapterNumber}`);
}

function cleanupTestChapter() {
  const chapterDir = getTestChapterDir();
  if (fs.existsSync(chapterDir)) {
    fs.rmSync(chapterDir, { recursive: true, force: true });
    logInfo(`Cleaned up test chapter directory: ${chapterDir}`);
  }
}

// Test suite
async function runTests() {
  const results = {
    passed: 0,
    failed: 0,
    tests: []
  };

  function recordTest(name, passed, error = null) {
    results.tests.push({ name, passed, error });
    if (passed) {
      results.passed++;
    } else {
      results.failed++;
    }
  }

  try {
    logSection('VPS FLOW INTEGRATION TEST');
    logInfo(`Manga: ${TEST_CONFIG.mangaSlug}`);
    logInfo(`Chapter: ${TEST_CONFIG.chapterNumber}`);
    logInfo(`Base URL: ${TEST_CONFIG.baseUrl}`);

    // Clean up any previous test data
    logSection('SETUP');
    logTest('Cleaning up previous test data');
    cleanupTestChapter();
    recordTest('Cleanup', true);

    // Test 1: Check server health
    logSection('TEST 1: Server Health Check');
    logTest('Testing /api/health endpoint');
    try {
      const healthResponse = await fetchWithTimeout(`${TEST_CONFIG.baseUrl}/api/health`);
      assertEqual(healthResponse.status, 200, 'Health endpoint returns 200');

      const healthData = await healthResponse.json();
      assertTruthy(healthData.status === 'ok', 'Health status is ok');
      recordTest('Server Health Check', true);
    } catch (error) {
      logError(`Server health check failed: ${error.message}`);
      recordTest('Server Health Check', false, error);
      throw error;
    }

    // Test 2: Get chapter images (scraping)
    logSection('TEST 2: Get Chapter Images');
    logTest('Fetching chapter images from scraper');
    try {
      const chapterSlug = `${TEST_CONFIG.mangaSlug}-${TEST_CONFIG.chapterNumber}`;
      const imagesResponse = await fetchWithTimeout(
        `${TEST_CONFIG.baseUrl}/api/chapter/${chapterSlug}/images?force=true`
      );

      assertEqual(imagesResponse.status, 200, 'Images endpoint returns 200');

      const imagesData = await imagesResponse.json();
      assertTruthy(Array.isArray(imagesData), 'Images data is an array');
      assertTruthy(imagesData.length > 0, 'Images array is not empty');

      logInfo(`Found ${imagesData.length} images`);
      logInfo(`First image: ${imagesData[0].substring(0, 60)}...`);
      recordTest('Get Chapter Images', true);

      // Save image URLs for download test
      const imageUrls = imagesData;
    } catch (error) {
      logError(`Failed to get chapter images: ${error.message}`);
      recordTest('Get Chapter Images', false, error);
      throw error;
    }

    // Test 3: Download chapter to storage
    logSection('TEST 3: Download Chapter to Storage');
    logTest('Downloading chapter images to VPS storage');
    try {
      const chapterSlug = `${TEST_CONFIG.mangaSlug}-${TEST_CONFIG.chapterNumber}`;
      const imagesResponse = await fetchWithTimeout(
        `${TEST_CONFIG.baseUrl}/api/chapter/${chapterSlug}/images?force=true`
      );
      const imagesData = await imagesResponse.json();

      // Download to storage using storage service
      logInfo('Starting download to storage...');
      const downloadResult = await storage.downloadChapter(
        TEST_CONFIG.mangaSlug,
        TEST_CONFIG.chapterNumber,
        imagesData.slice(0, 5) // Download only first 5 for faster testing
      );

      assertTruthy(downloadResult.success, 'Download completed successfully');
      logInfo(`Downloaded: ${downloadResult.downloaded} pages`);
      logInfo(`Failed: ${downloadResult.failed} pages`);

      recordTest('Download Chapter', true);
    } catch (error) {
      logError(`Failed to download chapter: ${error.message}`);
      recordTest('Download Chapter', false, error);
      throw error;
    }

    // Test 4: Verify chapter is in storage
    logSection('TEST 4: Verify Storage');
    logTest('Checking if chapter exists in storage');
    try {
      const isDownloaded = storage.isChapterDownloaded(
        TEST_CONFIG.mangaSlug,
        TEST_CONFIG.chapterNumber
      );
      assertTruthy(isDownloaded, 'Chapter exists in storage');
      recordTest('Chapter in Storage', true);

      logTest('Reading chapter manifest');
      const manifest = storage.getChapterManifest(
        TEST_CONFIG.mangaSlug,
        TEST_CONFIG.chapterNumber
      );

      assertTruthy(manifest !== null, 'Manifest exists');
      assertEqual(manifest.mangaSlug, TEST_CONFIG.mangaSlug, 'Manifest manga slug matches');
      assertEqual(manifest.chapterNumber, TEST_CONFIG.chapterNumber, 'Manifest chapter number matches');
      assertTruthy(manifest.totalPages > 0, 'Manifest has pages');

      logInfo(`Total pages in manifest: ${manifest.totalPages}`);
      logInfo(`Download date: ${manifest.downloadDate}`);
      logInfo(`Total size: ${(manifest.totalSize / 1024).toFixed(2)} KB`);

      recordTest('Manifest Validation', true);
    } catch (error) {
      logError(`Storage verification failed: ${error.message}`);
      recordTest('Storage Verification', false, error);
      throw error;
    }

    // Test 5: Verify image files exist
    logSection('TEST 5: Verify Image Files');
    logTest('Checking if image files exist on disk');
    try {
      const chapterDir = getTestChapterDir();
      assertTruthy(fs.existsSync(chapterDir), 'Chapter directory exists');

      const files = fs.readdirSync(chapterDir);
      const imageFiles = files.filter(f => f.endsWith('.jpg') || f.endsWith('.png'));

      logInfo(`Found ${imageFiles.length} image files in directory`);
      assertTruthy(imageFiles.length > 0, 'Image files exist');

      // Check that at least one image has content (some may be empty due to redirects)
      let totalSize = 0;
      let validImages = 0;
      imageFiles.forEach(file => {
        const imagePath = path.join(chapterDir, file);
        const stats = fs.statSync(imagePath);
        totalSize += stats.size;
        if (stats.size > 0) validImages++;
      });

      logInfo(`Valid images (non-empty): ${validImages}/${imageFiles.length}`);
      logInfo(`Total size: ${(totalSize / 1024).toFixed(2)} KB`);

      assertTruthy(validImages > 0, 'At least one image has content');

      recordTest('Image Files Verification', true);
    } catch (error) {
      logError(`Image file verification failed: ${error.message}`);
      recordTest('Image Files Verification', false, error);
      throw error;
    }

    // Test 6: Get image path from storage
    logSection('TEST 6: Get Image Path');
    logTest('Retrieving image path from storage service');
    try {
      const imagePath = storage.getImagePath(
        TEST_CONFIG.mangaSlug,
        TEST_CONFIG.chapterNumber,
        1
      );

      assertTruthy(imagePath !== null, 'Image path found');
      assertTruthy(fs.existsSync(imagePath), 'Image file exists at path');

      logInfo(`Image path: ${imagePath}`);

      recordTest('Get Image Path', true);
    } catch (error) {
      logError(`Failed to get image path: ${error.message}`);
      recordTest('Get Image Path', false, error);
      throw error;
    }

    // Test 7: List downloaded chapters
    logSection('TEST 7: List Downloaded Chapters');
    logTest('Listing all downloaded chapters for manga');
    try {
      const chapters = storage.listDownloadedChapters(TEST_CONFIG.mangaSlug);

      logInfo(`Found ${chapters.length} downloaded chapters`);
      assertTruthy(chapters.length > 0, 'At least one chapter downloaded');

      const testChapter = chapters.find(ch => ch.chapterNumber === TEST_CONFIG.chapterNumber);
      assertTruthy(testChapter !== undefined, 'Test chapter is in the list');

      logInfo(`Chapter ${testChapter.chapterNumber}: ${testChapter.totalSizeMB} MB`);
      logInfo(`Download date: ${testChapter.downloadDate}`);

      recordTest('List Chapters', true);
    } catch (error) {
      logError(`Failed to list chapters: ${error.message}`);
      recordTest('List Chapters', false, error);
      throw error;
    }

    // Test 8: Get storage stats
    logSection('TEST 8: Storage Statistics');
    logTest('Retrieving storage statistics');
    try {
      const stats = storage.getStorageStats();

      logInfo(`Total mangas: ${stats.totalMangas}`);
      logInfo(`Total chapters: ${stats.totalChapters}`);
      logInfo(`Total size: ${(stats.totalSize / 1024 / 1024).toFixed(2)} MB`);

      assertTruthy(stats.totalMangas > 0, 'At least one manga in storage');
      assertTruthy(stats.totalChapters > 0, 'At least one chapter in storage');

      const testManga = stats.mangaDetails.find(m => m.mangaSlug === TEST_CONFIG.mangaSlug);
      assertTruthy(testManga !== undefined, 'Test manga is in stats');

      logInfo(`Manga "${TEST_CONFIG.mangaSlug}": ${testManga.chapters} chapters, ${testManga.totalSizeMB} MB`);

      recordTest('Storage Stats', true);
    } catch (error) {
      logError(`Failed to get storage stats: ${error.message}`);
      recordTest('Storage Stats', false, error);
      throw error;
    }

    // Test 9: Delete chapter
    logSection('TEST 9: Delete Chapter');
    logTest('Deleting chapter from storage');
    try {
      const deleteResult = storage.deleteChapter(
        TEST_CONFIG.mangaSlug,
        TEST_CONFIG.chapterNumber
      );

      assertTruthy(deleteResult.success, 'Delete operation succeeded');
      logInfo('Chapter deleted successfully');

      recordTest('Delete Chapter', true);
    } catch (error) {
      logError(`Failed to delete chapter: ${error.message}`);
      recordTest('Delete Chapter', false, error);
      throw error;
    }

    // Test 10: Verify deletion
    logSection('TEST 10: Verify Deletion');
    logTest('Verifying chapter was removed');
    try {
      const isDownloaded = storage.isChapterDownloaded(
        TEST_CONFIG.mangaSlug,
        TEST_CONFIG.chapterNumber
      );

      assertTruthy(!isDownloaded, 'Chapter no longer exists in storage');

      const manifest = storage.getChapterManifest(
        TEST_CONFIG.mangaSlug,
        TEST_CONFIG.chapterNumber
      );

      assertTruthy(manifest === null, 'Manifest is null after deletion');

      // Note: Directory may still exist but manifest is gone (which is what matters)
      logInfo('Chapter successfully removed from storage');

      recordTest('Verify Deletion', true);
    } catch (error) {
      logError(`Deletion verification failed: ${error.message}`);
      recordTest('Verify Deletion', false, error);
      throw error;
    }

    // Test 11: Verify storage stats updated
    logSection('TEST 11: Verify Storage Stats Updated');
    logTest('Checking storage stats after deletion');
    try {
      const stats = storage.getStorageStats();

      logInfo(`Total mangas after deletion: ${stats.totalMangas}`);
      logInfo(`Total chapters after deletion: ${stats.totalChapters}`);

      const testManga = stats.mangaDetails.find(m => m.mangaSlug === TEST_CONFIG.mangaSlug);

      if (testManga) {
        logInfo(`Manga "${TEST_CONFIG.mangaSlug}": ${testManga.chapters} chapters`);
        const testChapter = testManga.chapters === 0 || !testManga.chapters.includes(TEST_CONFIG.chapterNumber);
        logInfo('Test chapter removed from stats');
      } else {
        logInfo('Manga removed from stats (no chapters left)');
      }

      recordTest('Stats Updated', true);
    } catch (error) {
      logError(`Stats verification failed: ${error.message}`);
      recordTest('Stats Updated', false, error);
      throw error;
    }

  } catch (error) {
    logError(`\n❌ Test suite failed: ${error.message}`);
    console.error(error.stack);
  }

  // Print results summary
  logSection('TEST RESULTS SUMMARY');
  console.log(`\nTotal Tests: ${results.tests.length}`);
  log(`Passed: ${results.passed}`, 'green');
  log(`Failed: ${results.failed}`, 'red');

  if (results.failed > 0) {
    console.log('\nFailed Tests:');
    results.tests
      .filter(t => !t.passed)
      .forEach(t => {
        logError(`  - ${t.name}`);
        if (t.error) {
          console.log(`    Error: ${t.error.message}`);
        }
      });
  }

  console.log('\n' + '='.repeat(70));

  if (results.failed === 0) {
    log('🎉 ALL TESTS PASSED!', 'green');
    process.exit(0);
  } else {
    log('❌ SOME TESTS FAILED', 'red');
    process.exit(1);
  }
}

// Run tests
console.log('\n🚀 Starting VPS Flow Integration Tests...\n');
runTests().catch(error => {
  console.error('\n❌ Fatal error:', error);
  process.exit(1);
});
