#!/usr/bin/env tsx
/**
 * Comprehensive Test Suite
 * Validates all systems, data, and components
 * 
 * Run with: npx tsx comprehensive-test.ts
 */

console.log('🧪 SUNO RPG COMPREHENSIVE TEST SUITE');
console.log('='.repeat(50));
console.log('');

let totalTests = 0;
let passedTests = 0;
let failedTests = 0;

function test(name: string, fn: () => void | Promise<void>): void {
  totalTests++;
  try {
    const result = fn();
    if (result instanceof Promise) {
      result
        .then(() => {
          console.log(`✅ ${name}`);
          passedTests++;
        })
        .catch((error: unknown) => {
          console.error(`❌ ${name}`);
          const errorMessage = error instanceof Error ? error.message : String(error);
          console.error(`   Error: ${errorMessage}`);
          failedTests++;
        });
    } else {
      console.log(`✅ ${name}`);
      passedTests++;
    }
  } catch (error: unknown) {
    console.error(`❌ ${name}`);
    const errorMessage = error instanceof Error ? error.message : String(error);
    console.error(`   Error: ${errorMessage}`);
    failedTests++;
  }
}

// Main test runner
async function runTests() {
  // ============ DATA TESTS ============
  console.log('📚 Data Integrity Tests');
  console.log('-'.repeat(50));

  const { musicians, musicianChallengeLookup } = await import('./src/data/musicians');
  const { genres } = await import('./src/data/genres');
  const { instruments, materials } = await import('./src/data/instruments');

  test('Musicians database has 100+ entries', () => {
    if (musicians.length < 100) throw new Error(`Only ${musicians.length} musicians`);
  });

  test('All musicians have required fields', () => {
    musicians.forEach((m: any) => {
      if (!m.id || !m.name || !m.era || !m.genres) {
        throw new Error(`Musician ${m.id || 'unknown'} missing required fields`);
      }
    });
  });

  test('No duplicate musician IDs', () => {
    const ids = musicians.map((m: any) => m.id);
    const uniqueIds = new Set(ids);
    if (ids.length !== uniqueIds.size) {
      throw new Error('Duplicate musician IDs found');
    }
  });

  test('Genres database has 30+ entries', () => {
    if (genres.length < 30) throw new Error(`Only ${genres.length} genres`);
  });

  test('All genres have valid skill trees', () => {
    genres.forEach((g: any) => {
      if (!g.skillTree || g.skillTree.length === 0) {
        throw new Error(`Genre ${g.id} has no skills`);
      }
      
      g.skillTree.forEach((skill: any) => {
        if (skill.prerequisite) {
          const prereqExists = g.skillTree.some((s: any) => s.id === skill.prerequisite);
          if (!prereqExists) {
            throw new Error(`Invalid prerequisite in ${g.id}: ${skill.prerequisite}`);
          }
        }
      });
    });
  });

  test('All genres have 10 XP requirements', () => {
    genres.forEach((g: any) => {
      if (!g.xpRequirements || g.xpRequirements.length !== 10) {
        throw new Error(`Genre ${g.id} has invalid XP requirements`);
      }
    });
  });

  test('Instruments database has 20+ entries', () => {
    if (instruments.length < 20) throw new Error(`Only ${instruments.length} instruments`);
  });

  test('All instrument materials exist', () => {
    instruments.forEach((inst: any) => {
      if (inst.craftingMaterials) {
        inst.craftingMaterials.forEach((mat: any) => {
          if (!materials[mat.item]) {
            throw new Error(`Unknown material: ${mat.item} in ${inst.id}`);
          }
        });
      }
    });
  });

  console.log('');

  // ============ SYSTEM TESTS ============
  console.log('⚙️  System Functionality Tests');
  console.log('-'.repeat(50));

  test('All stores exist', async () => {
    await import('./src/stores/gameStore');
    await import('./src/stores/uiStore');
    await import('./src/stores/settingsStore');
  });

  test('All system managers exist', async () => {
    await import('./src/systems/GenreManager');
    await import('./src/systems/InventoryManager');
    await import('./src/systems/EventManager');
    await import('./src/systems/ReputationManager');
    await import('./src/systems/QuestEngine');
    await import('./src/systems/SaveManager');
  });

  test('All data files load correctly', async () => {
    await import('./src/data/worldEvents');
    await import('./src/data/questlines');
    await import('./src/data/specialLocations');
    await import('./src/data/styleFusions');
    await import('./src/data/tutorialSteps');
  });

  console.log('');

  // ============ COVERAGE TESTS ============
  console.log('📊 Feature Coverage Tests');
  console.log('-'.repeat(50));

  test('Musicians cover all eras', () => {
    const eras = ['1720s', '1780s', '1800s', '1830s', '1920s', '1930s', '1940s', 
                  '1950s', '1960s', '1970s', '1980s', '1990s', '2000s', '2010s', '2020s'];
    
    eras.forEach((era: string) => {
      const musiciansInEra = musicians.filter((m: any) => m.era === era);
      if (musiciansInEra.length === 0 && era !== '1890s') {
        throw new Error(`No musicians in era: ${era}`);
      }
    });
  });

  test('Musicians represent diverse genres', () => {
    const genreCount: Record<string, number> = {};
    musicians.forEach((m: any) => {
      m.genres.forEach((g: string) => {
        genreCount[g] = (genreCount[g] || 0) + 1;
      });
    });
    
    if (Object.keys(genreCount).length < 15) {
      throw new Error('Not enough genre diversity');
    }
  });

  test('Key spotlight artists expose mentor tips and recommended challenges', () => {
    const requiredIds = [
      'mozart',
      'elvis',
      'hendrix',
      'beatles',
      'queen',
      'michael_jackson',
      'madonna',
      'miles_davis'
    ];

    requiredIds.forEach((id) => {
      const musician = musicians.find((m: any) => m.id === id);
      if (!musician) {
        throw new Error(`Missing musician data for ${id}`);
      }
      if (!musician.mentorTip || !Array.isArray(musician.recommendedChallenges) || musician.recommendedChallenges.length === 0) {
        throw new Error(`Musician ${id} should expose mentor tips and recommended challenges`);
      }
    });
  });

  test('Instruments cover all types', () => {
    const types = new Set(instruments.map((i: any) => i.type));
    const expectedTypes = ['guitar', 'keyboard', 'drums', 'brass', 'microphone', 'studio'];
    
    expectedTypes.forEach((type: string) => {
      if (!types.has(type)) {
        console.warn(`   ⚠️  No instruments of type: ${type}`);
      }
    });
  });

  const { worldEvents } = await import('./src/data/worldEvents');
  const { questlines } = await import('./src/data/questlines');
  const { specialLocations } = await import('./src/data/specialLocations');
  const { styleFusions } = await import('./src/data/styleFusions');

  test('Events exist for multiple eras', () => {
    const eventEras = new Set(worldEvents.map((e: any) => e.era || e.spawnConditions?.timePeriod));
    if (eventEras.size < 5) {
      throw new Error('Not enough era diversity in events');
    }
  });

  test('Questline recommended challenges are backed by musician data', () => {
    questlines.forEach((quest: any) => {
      quest.steps?.forEach((step: any) => {
        const ids: string[] | undefined = step?.objective?.recommendedChallengeIds;
        if (!Array.isArray(ids)) {
          return;
        }
        ids.forEach((challengeId: string) => {
          const isMapped = Object.values(musicianChallengeLookup).some((challengeList: string[]) =>
            Array.isArray(challengeList) && challengeList.includes(challengeId)
          );
          if (!isMapped) {
            throw new Error(`Quest ${quest.id} references unknown challenge ${challengeId}`);
          }
        });
      });
    });
  });

  test('Quests exist and are valid', () => {
    if (questlines.length < 3) {
      throw new Error('Not enough questlines');
    }
    
    questlines.forEach((q: any) => {
      if (!q.steps || q.steps.length === 0) {
        throw new Error(`Quest ${q.id} has no steps`);
      }
    });
  });

  test('Quest recommended challenges map to known musician challenges', () => {
    const musicianChallengeIds = new Set<string>();
    musicians.forEach((musician: any) => {
      if (Array.isArray(musician.recommendedChallenges)) {
        musician.recommendedChallenges.forEach((challenge: any) => {
          if (challenge?.id) {
            musicianChallengeIds.add(challenge.id);
          }
        });
      }
    });

    questlines.forEach((quest: any) => {
      quest.steps?.forEach((step: any) => {
        const recommendations = step?.objective?.recommendedChallengeIds;
        if (Array.isArray(recommendations)) {
          recommendations.forEach((challengeId: string) => {
            if (!musicianChallengeIds.has(challengeId)) {
              throw new Error(
                `Quest ${quest.id} references unknown challenge '${challengeId}'`
              );
            }
          });
        }
      });
    });
  });

  test('Special locations exist', () => {
    if (specialLocations.length < 10) {
      throw new Error('Not enough special locations');
    }
    
    const types = new Set(specialLocations.map((l: any) => l.type));
    if (types.size < 4) {
      throw new Error('Not enough location type diversity');
    }
  });

  test('Style fusions are balanced', () => {
    if (styleFusions.length < 15) {
      throw new Error('Not enough fusion recipes');
    }
    
    styleFusions.forEach((f: any) => {
      if (!f.ingredients || f.ingredients.length < 2) {
        throw new Error(`Fusion ${f.id} needs 2+ ingredients`);
      }
    });
  });

  console.log('');

  // ============ STATISTICS ============
  console.log('📈 Final Statistics');
  console.log('-'.repeat(50));

  console.log(`Musicians: ${musicians.length}`);
  console.log(`Genres: ${genres.length}`);
  console.log(`Instruments: ${instruments.length}`);
  console.log(`World Events: ${worldEvents.length}`);
  console.log(`Questlines: ${questlines.length}`);
  console.log(`Special Locations: ${specialLocations.length}`);
  console.log(`Style Fusions: ${styleFusions.length}`);

  const totalSkills = genres.reduce((sum: number, g: any) => sum + g.skillTree.length, 0);
  console.log(`Total Skills: ${totalSkills}`);

  console.log('');

  // ============ RESULTS ============
  // Wait a bit for async tests to complete
  await new Promise(resolve => setTimeout(resolve, 1000));

  console.log('='.repeat(50));
  console.log('📊 TEST RESULTS');
  console.log('='.repeat(50));
  console.log(`Total Tests: ${totalTests}`);
  console.log(`Passed: ${passedTests} ✅`);
  console.log(`Failed: ${failedTests} ❌`);
  console.log(`Success Rate: ${Math.round((passedTests/totalTests) * 100)}%`);
  console.log('');

  if (failedTests === 0) {
    console.log('🎉 ALL TESTS PASSED! System is production-ready!');
    process.exit(0);
  } else {
    console.log('⚠️  Some tests failed. Review errors above.');
    process.exit(1);
  }
}

// Run the tests
runTests().catch((error) => {
  console.error('Fatal error running tests:', error);
  process.exit(1);
});

