import 'dart:convert';
import 'dart:io';

import 'package:crypto/crypto.dart' as crypto;
import 'package:flutter/foundation.dart';

import 'Backend.dart';
import 'DebugHelper.dart';
import 'package:path/path.dart' as Path;
import 'package:http/http.dart' as http;

import 'FileHashEntry.dart';
class InstallHelper{

  static String GetFilePath(int id) => Backend.prefs!.getString('${Backend.Games[id].name}_path') ?? '${Path.join(Backend.docPath!.path, Backend.Games[id].name)}';

  static String GetValidationSaveKey(int id)=>'${Backend.Games[id].code}_localFileData';
  static String? GetSavedValidationData(int id) => Backend.prefs!.getString(GetValidationSaveKey(id));

  static Future<List<FileHashEntry>> GetMissingFiles(int gameId, {bool force = false}) async{
    List<FileHashEntry> missingFiles = [];
    Directory pathDir = Directory(GetFilePath(gameId));

    List<FileHashEntry> localFiles = [];
    bool usedLocalSave = false;
    if(!force){
      String? savedData =GetSavedValidationData(gameId);
      if(savedData != null){
        localFiles = FileHashEntry.listFromJson(savedData);
        usedLocalSave=true;
      }else{
        localFiles = await getHashtable(pathDir);
      }
    }else{
      localFiles = await getHashtable(pathDir);
    }
    //Get local hashtable
    // Debug.Log(localFilesJson);
    //Write to file
    if(!usedLocalSave){
      String localFilesJson = FileHashEntry.listToJson(localFiles);
      Backend.prefs!.setString(GetValidationSaveKey(gameId),localFilesJson);
      if(kDebugMode) {
        File writeJsonFile = File(Path.join(pathDir.parent.path, "hashes.txt"));
        writeJsonFile.writeAsString(localFilesJson);
      }
    }

    //Get remote hashtable
    String link ='${Backend.API_ENDPOINT}${Backend.Games[gameId].code}/get_hashes.php';
    var remoteHashResponse = (await http.post(Uri.parse(link)));
    Debug.LogResponse(remoteHashResponse.body.toString(),src: link);
    List<FileHashEntry> remoteList = FileHashEntry.listFromJson(remoteHashResponse.body.toString());

    //Comapreeee
    for(int i = 0; i <remoteList.length;i++) {
      bool isLocalGood = false;
      for (int j = 0; j < localFiles.length; j++) {
        if (localFiles[j].file == remoteList[i].file) {
          isLocalGood = localFiles[j].hash == remoteList[i].hash;
          break;
        }
      }

      if (!isLocalGood) {
        missingFiles.add(remoteList[i]);
      }
    }

    return missingFiles;
  }

  static Future<List<FileHashEntry>> getHashtable(Directory pathDir) async{

    if(!pathDir.existsSync()){return [];}
    List<FileSystemEntity> list =await pathDir.list(recursive: true).toList();
    List<FileHashEntry> output = [];

    for(int i =0; i < list.length; i++){
      FileSystemEntity element = list[i];
      File file = File(element.path);
      if(await file.exists()) {
        String hash = await calculateMD5SumAsyncWithCrypto(element.path);
        int size = file.lengthSync();
        output.add(FileHashEntry(element.path.replaceAll(pathDir.path, ''), hash, size));
        // Debug.Log('${element.path}: ${hash}: ${size}');
      }
    }

    return output;
  }

  static String DOWNLOAD_BIN_ROOT = "https://vps.playpoolstudios.com/metahunt/data/bin/";

  static List<String> GetMissingFileLinks(List<FileHashEntry> missingList,int gameId){
    List<String> output = [];
    missingList.forEach((element) {
      output.add(GetFileUrl(element.file, gameId));
    });

    return output;
  }

  static String GetFileUrl(String path, int id){
    return '${DOWNLOAD_BIN_ROOT}${Backend.Games[id].code}${path.replaceAll('\\', '/')}';
  }

  static Future<String> calculateMD5SumAsyncWithCrypto(String filePath) async {
    var ret = '';
    var file = File(filePath);
    if (await file.exists()) {
      try {
        var md5 = crypto.md5;
        var hash = await md5.bind(file.openRead()).first;
        ret = hash.toString();
        // ret = base64.encode(hash.bytes);
      } catch (exception) {
        print('Unable to evaluate the MD5 sum :$exception');
        return '';
      }
    } else {
      print('`$filePath` does not exits so unable to evaluate its MD5 sum.');
      return '';
    }
    return ret;
  }


  static String GetExeFilePath(int i){
    return Path.join(GetFilePath(i), Backend.Games[i].exeName);
  }
}


