import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';

import 'package:crypto/crypto.dart';
import 'package:wolf_3d_dart/src/data/wl_parser.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';

/// dart:io implementation for directory discovery with version integrity checks.
Future<Map<GameVersion, WolfensteinData>> discoverInDirectory({
  String? directoryPath,
  bool recursive = false,
}) async {
  final dir = Directory(directoryPath ?? Directory.current.path);
  if (!await dir.exists()) {
    log('Warning: Directory does not exist -> ${dir.path}');
    return {};
  }

  final allFiles = await dir
      .list(recursive: recursive)
      .where((entity) => entity is File)
      .cast<File>()
      .toList();

  final Map<GameVersion, WolfensteinData> loadedVersions = {};

  for (final version in GameVersion.values) {
    final ext = version.fileExtension.toUpperCase();
    final Map<GameFile, File> foundFiles = {};

    for (final requiredFile in GameFile.values) {
      final expectedName = '${requiredFile.baseName}.$ext';

      var match = allFiles.where((file) {
        final fileName = file.uri.pathSegments.last.toUpperCase();
        return fileName == expectedName;
      }).firstOrNull;

      // v1.0 FIX: In the very first retail release, the maps were named
      // MAPTEMP.WL6 instead of GAMEMAPS.WL6. We check for this alias
      // to ensure v1.0 data is properly discovered.
      if (match == null && requiredFile == GameFile.gameMaps) {
        final altName = 'MAPTEMP.$ext';
        match = allFiles.where((file) {
          final fileName = file.uri.pathSegments.last.toUpperCase();
          return fileName == altName;
        }).firstOrNull;
      }

      if (match != null) {
        foundFiles[requiredFile] = match;
      }
    }

    if (foundFiles.isEmpty) continue;

    // Validation for missing files
    if (foundFiles.length < GameFile.values.length) {
      final missingFiles = GameFile.values
          .where((f) => !foundFiles.containsKey(f))
          .map((f) => '${f.baseName}.$ext')
          .join(', ');
      log(
        'Found partial data for ${version.name}. Missing: $missingFiles',
      );
      continue;
    }

    try {
      // 1. Read VSWAP to determine the precise DataVersion identity
      final vswapFile = foundFiles[GameFile.vswap]!;
      final vswapBytes = await vswapFile.readAsBytes();

      // 2. Generate Checksum and Resolve Identity
      final hash = md5.convert(vswapBytes).toString();
      final identity = DataVersion.fromChecksum(hash);

      log('--- Found ${version.name} ---');
      log('MD5 Identity: ${identity.name} ($hash)');
      if (identity == DataVersion.version10Retail) {
        log(
          'Note: Detected v1.0 specific file structure (MAPTEMP support active).',
        );
      }

      // 3. Load the data using the parser
      final data = WLParser.load(
        version: version,
        dataIdentity: identity,
        vswap: vswapBytes.buffer.asByteData(),
        mapHead: await _readFile(foundFiles[GameFile.mapHead]!),
        gameMaps: await _readFile(foundFiles[GameFile.gameMaps]!),
        vgaDict: await _readFile(foundFiles[GameFile.vgaDict]!),
        vgaHead: await _readFile(foundFiles[GameFile.vgaHead]!),
        vgaGraph: await _readFile(foundFiles[GameFile.vgaGraph]!),
        audioHed: await _readFile(foundFiles[GameFile.audioHed]!),
        audioT: await _readFile(foundFiles[GameFile.audioT]!),
      );

      loadedVersions[version] = data;
    } catch (e) {
      log('Error parsing data for ${version.name}: $e');
    }
  }

  return loadedVersions;
}

Future<ByteData> _readFile(File file) async {
  final bytes = await file.readAsBytes();
  return bytes.buffer.asByteData();
}
