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

import 'package:cabo_counter/data/game_manager.dart';
import 'package:cabo_counter/data/game_session.dart';
import 'package:file_picker/file_picker.dart';
import 'package:file_saver/file_saver.dart';
import 'package:flutter/services.dart';
import 'package:json_schema/json_schema.dart';
import 'package:path_provider/path_provider.dart';

enum ImportStatus {
  success,
  canceled,
  validationError,
  formatError,
  genericError
}

class LocalStorageService {
  static const String _fileName = 'game_data.json';

  /// Writes the game session list to a JSON file and returns it as string.
  static String _getGameDataAsJsonFile() {
    final jsonFile =
        gameManager.gameList.map((session) => session.toJson()).toList();
    return json.encode(jsonFile);
  }

  /// Returns the path to the local JSON file.
  static Future<File> _getFilePath() async {
    final directory = await getApplicationDocumentsDirectory();
    final path = '${directory.path}/$_fileName';
    return File(path);
  }

  /// Saves the game sessions to a local JSON file.
  static Future<void> saveGameSessions() async {
    print('[local_storage_service.dart] Versuche, Daten zu speichern...');
    try {
      final file = await _getFilePath();
      final jsonFile = _getGameDataAsJsonFile();
      await file.writeAsString(jsonFile);
      print(
          '[local_storage_service.dart] Die Spieldaten wurden zwischengespeichert.');
    } catch (e) {
      print(
          '[local_storage_service.dart] Fehler beim Speichern der Spieldaten. Exception: $e');
    }
  }

  /// Loads the game data from a local JSON file.
  static Future<bool> loadGameSessions() async {
    print('[local_storage_service.dart] Versuche, Daten zu laden...');
    try {
      final file = await _getFilePath();

      if (!await file.exists()) {
        print(
            '[local_storage_service.dart] Es existiert noch keine Datei mit Spieldaten');
        return false;
      }

      print(
          '[local_storage_service.dart] Es existiert bereits eine Datei mit Spieldaten');
      final jsonString = await file.readAsString();

      if (jsonString.isEmpty) {
        print('[local_storage_service.dart] Die gefundene Datei ist leer');
        return false;
      }

      if (!await _validateJsonSchema(jsonString, true)) {
        print(
            '[local_storage_service.dart] Die Datei konnte nicht validiert werden');
        gameManager.gameList = [];
        return false;
      }
      print('[local_storage_service.dart] Die gefundene Datei hat Inhalt');
      print(
          '[local_storage_service.dart] Die gefundene Datei wurde erfolgreich validiert');
      final jsonList = json.decode(jsonString) as List<dynamic>;

      gameManager.gameList = jsonList
          .map((jsonItem) =>
              GameSession.fromJson(jsonItem as Map<String, dynamic>))
          .toList();

      for (GameSession session in gameManager.gameList) {
        print(
            '[local_storage_service.dart] Geladene Session: ${session.gameTitle} - ${session.id}');
      }

      print(
          '[local_storage_service.dart] Die Spieldaten wurden erfolgreich geladen und verarbeitet');
      return true;
    } catch (e) {
      print(
          '[local_storage_service.dart] Fehler beim Laden der Spieldaten:\n$e');
      gameManager.gameList = [];
      return false;
    }
  }

  /// Opens the file picker to export game data as a JSON file.
  /// This method will export the given [jsonString] as a JSON file. It opens
  /// the file picker with the choosen [fileName].
  static Future<bool> _exportJsonData(
    String jsonString,
    String fileName,
  ) async {
    try {
      final bytes = Uint8List.fromList(utf8.encode(jsonString));
      final path = await FileSaver.instance.saveAs(
        name: fileName,
        bytes: bytes,
        ext: 'json',
        mimeType: MimeType.json,
      );
      if (path == null) {
        print('[local_storage_service.dart]: Export abgebrochen');
      } else {
        print(
            '[local_storage_service.dart] Die Spieldaten wurden exportiert. Dateipfad: $path');
      }
      return true;
    } catch (e) {
      print(
          '[local_storage_service.dart] Fehler beim Exportieren der Spieldaten. Exception: $e');
      return false;
    }
  }

  /// Opens the file picker to export all game sessions as a JSON file.
  static Future<bool> exportGameData() async {
    String jsonString = _getGameDataAsJsonFile();
    String fileName = 'cabo_counter-game_data';
    return _exportJsonData(jsonString, fileName);
  }

  /// Opens the file picker to save a single game session as a JSON file.
  static Future<bool> exportSingleGameSession(GameSession session) async {
    String jsonString = json.encode(session.toJson());
    String fileName = 'cabo_counter-game_${session.id.substring(0, 7)}';
    return _exportJsonData(jsonString, fileName);
  }

  /// Opens the file picker to import a JSON file and loads the game data from it.
  static Future<ImportStatus> importJsonFile() async {
    final path = await FilePicker.platform.pickFiles(
      dialogTitle: 'Wähle eine Datei mit Spieldaten aus',
      type: FileType.custom,
      allowedExtensions: ['json'],
    );

    if (path == null) {
      print(
          '[local_storage_service.dart] Der Filepicker-Dialog wurde abgebrochen');
      return ImportStatus.canceled;
    }

    try {
      final jsonString = await _readFileContent(path.files.single);

      if (await _validateJsonSchema(jsonString, true)) {
        // Checks if the JSON String is in the gameList format

        final jsonData = json.decode(jsonString) as List<dynamic>;
        List<GameSession> importedList = jsonData
            .map((jsonItem) =>
                GameSession.fromJson(jsonItem as Map<String, dynamic>))
            .toList();

        for (GameSession s in importedList) {
          _importSession(s);
        }
      } else if (await _validateJsonSchema(jsonString, false)) {
        // Checks if the JSON String is in the single game format
        final jsonData = json.decode(jsonString) as Map<String, dynamic>;
        _importSession(GameSession.fromJson(jsonData));
      } else {
        return ImportStatus.validationError;
      }

      print(
          '[local_storage_service.dart] Die Datei wurde erfolgreich Importiert');
      await saveGameSessions();
      return ImportStatus.success;
    } on FormatException catch (e) {
      print(
          '[local_storage_service.dart] Ungültiges JSON-Format. Exception: $e');
      return ImportStatus.formatError;
    } on Exception catch (e) {
      print(
          '[local_storage_service.dart] Fehler beim Dateizugriff. Exception: $e');
      return ImportStatus.genericError;
    }
  }

  /// Imports a single game session into the gameList.
  static Future<void> _importSession(GameSession session) async {
    if (gameManager.gameExistsInGameList(session.id)) {
      print(
          '[local_storage_service.dart] Die Session mit der ID ${session.id} existiert bereits. Sie wird überschrieben.');
      gameManager.removeGameSessionById(session.id);
    }
    gameManager.addGameSession(session);
    print(
        '[local_storage_service.dart] Die Session mit der ID ${session.id} wurde erfolgreich importiert.');
  }

  /// Helper method to read file content from either bytes or path
  static Future<String> _readFileContent(PlatformFile file) async {
    if (file.bytes != null) return utf8.decode(file.bytes!);
    if (file.path != null) return await File(file.path!).readAsString();

    throw Exception('Die Datei hat keinen lesbaren Inhalt');
  }

  /// Validates the JSON data against the schema.
  /// This method checks if the provided [jsonString] is valid against the
  /// JSON schema. It takes a boolean [isGameList] to determine
  /// which schema to use (game list or single game).
  static Future<bool> _validateJsonSchema(
      String jsonString, bool isGameList) async {
    final String schemaString;

    if (isGameList) {
      schemaString =
          await rootBundle.loadString('assets/game_list-schema.json');
    } else {
      schemaString = await rootBundle.loadString('assets/game-schema.json');
    }

    try {
      final schema = JsonSchema.create(json.decode(schemaString));
      final jsonData = json.decode(jsonString);
      final result = schema.validate(jsonData);

      if (result.isValid) {
        print(
            '[local_storage_service.dart] JSON ist erfolgreich validiert. Typ: ${isGameList ? 'Game List' : 'Single Game'}');
        return true;
      }
      print(
          '[local_storage_service.dart] JSON ist nicht gültig.\nFehler: ${result.errors}');
      return false;
    } catch (e) {
      print(
          '[local_storage_service.dart] Fehler beim Validieren des JSON-Schemas: $e');
      return false;
    }
  }

  static Future<bool> deleteAllGames() async {
    try {
      gameManager.gameList.clear();
      await saveGameSessions();
      print(
          '[local_storage_service.dart] Alle Runden wurden erfolgreich gelöscht.');
      return true;
    } catch (e) {
      print(
          '[local_storage_service.dart] Fehler beim Löschen aller Runden: $e');
      return false;
    }
  }
}
