import 'package:hub/shared/utils/cache_util.dart';
import 'package:hub/shared/utils/storage_util.dart';
import 'package:sqflite/sqflite.dart';
import 'dart:developer';
import 'package:path/path.dart';

class DatabaseConfig {
  static const String dbName = 'database.db';
  static const int dbVersion = 1;

  static const String tableKeychain = 'keychain';
  static const String columnKey = 'key';
  static const String columnValue = 'value';
  static const String columnType = 'type';
  static const String columnUpdateAt = 'updateAt';
  static const String columnResolvedAt = 'resolvedAt';
  static const String columnCreatedAt = 'createdAt';

  static const List<Map<String, dynamic>> initialData = [
    {'key': 'devUUID', 'value': '', 'type': 'user'},
    {'key': 'userUUID', 'value': '', 'type': 'user'},
    {'key': 'userDevUUID', 'value': '', 'type': 'user'},
    {'key': 'status', 'value': '', 'type': 'user'},
    {'key': 'userName', 'value': '', 'type': 'user'},
    {'key': 'cliUUID', 'value': '', 'type': 'local'},
    {'key': 'ownerUUID', 'value': '', 'type': 'local'},
    {'key': 'cliName', 'value': '', 'type': 'local'},
    {'key': 'whatsapp', 'value': 'false', 'type': 'util'},
    {'key': 'provisional', 'value': 'false', 'type': 'util'},
    {'key': 'pets', 'value': 'false', 'type': 'util'},
    {'key': 'local', 'value': 'false', 'type': 'util'},
    {'key': 'notify', 'value': 'false', 'type': 'util'},
    {'key': 'fingerprint', 'value': 'false', 'type': 'util'},
    {'key': 'access', 'value': 'false', 'type': 'util'},
    {'key': 'panic', 'value': 'false', 'type': 'util'},
    {'key': 'person', 'value': 'false', 'type': 'util'},
    {'key': 'requestOSnotification', 'value': 'false', 'type': 'util'},
    {'key': 'petAmountRegister', 'value': '', 'type': 'local'},
  ];
}

class SQLiteStorageHelper implements Storage {
  static final SQLiteStorageHelper _instance = SQLiteStorageHelper._internal();
  static Database? _database;
  static String? _databasePath;

  factory SQLiteStorageHelper() => _instance;

  SQLiteStorageHelper._internal();

  static SQLiteStorageHelper get instance => _instance;

  Future<Database> get database async {
    log('Getting database instance');
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  Future<String> _getDatabasePath() async {
    log('Getting database path');
    if (_databasePath != null) return _databasePath!;
    final databasesPath = await getDatabasesPath();
    _databasePath = join(databasesPath, DatabaseConfig.dbName);
    log('Database path: $_databasePath');
    return _databasePath!;
  }

  Future<Database> _initDatabase() async {
    log('Initializing database');
    final path = await _getDatabasePath();
    return await openDatabase(
      path,
      version: DatabaseConfig.dbVersion,
      onCreate: _onCreate,
      onOpen: _onOpen,
      onUpgrade: _onUpgrade,
      onDowngrade: _onDowngrade,
      onConfigure: _onConfigure,
    ).catchError((error) {
      log('Error initializing database: $error');
      throw error;
    }).whenComplete(() async {
      log('Database initialization complete');
      await setupLocalVariables();
    });
  }

  Future<void> _onCreate(Database db, int version) async {
    log('Creating tables...');
    await db.execute('''
      CREATE TABLE ${DatabaseConfig.tableKeychain} (
        ${DatabaseConfig.columnKey} TEXT UNIQUE,
        ${DatabaseConfig.columnValue} TEXT,
        ${DatabaseConfig.columnType} TEXT,
        ${DatabaseConfig.columnUpdateAt} TEXT,
        ${DatabaseConfig.columnResolvedAt} TEXT,
        ${DatabaseConfig.columnCreatedAt} TEXT
      );
    ''');
    await _insertInitialData(db);
    log('Tables created');
  }

  Future<void> _insertInitialData(Database db) async {
    log('Inserting initial data');
    final batch = db.batch();
    for (var data in DatabaseConfig.initialData) {
      batch.insert(DatabaseConfig.tableKeychain, data);
    }
    await batch.commit(noResult: true);
    log('Initial data inserted');
  }

  Future<void> _onOpen(Database db) async {
    log('Opening database');
    await _checkExistingData(db);
  }

  Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
    log('Upgrading database from version $oldVersion to $newVersion');
  }

  Future<void> _onDowngrade(Database db, int oldVersion, int newVersion) async {
    log('Downgrading database from version $oldVersion to $newVersion');
  }

  Future<void> _onConfigure(Database db) async {
    log('Configuring database');
  }

  Future<void> _checkExistingData(Database db) async {
    log('Checking existing data');
    try {
      final maps = await db.query(DatabaseConfig.tableKeychain);
      log('Existing data: $maps');
    } catch (error) {
      log('Error checking existing data: $error');
    }
  }

  Future<void> setupLocalVariables() async {
    log('Setting up local variables');
    try {
      await _database?.transaction((txn) async {
        final keys = [
          'devUUID',
          'userUUID',
          'userDevUUID',
          'status',
          'userName',
          'cliUUID',
          'ownerUUID',
          'cliName',
          'petAmountRegister',
          'whatsapp',
          'provisional',
          'pets',
          'local',
          'notify',
          'fingerprint',
          'access',
          'panic',
          'person',
          'requestOSnotification'
        ];

        for (var key in keys) {
          log('Fetching value for key: $key');
          final result = await txn.query(
            DatabaseConfig.tableKeychain,
            where: '${DatabaseConfig.columnKey} = ?',
            whereArgs: [key],
          );
          log('Result for key $key: $result');
        }
      });
      log('Local variables setup complete');
    } catch (error) {
      log('Error setting up local variables: $error');
    }
  }

  Future<bool> getBoolean(String key) async {
    log('Getting boolean value for key: $key');
    final value = await get(key);
    return value == 'true';
  }

  @override
  Future<dynamic> get(String key) async {
    log('Getting value for key: $key');
    final cachedValue = CacheUtil().get(key);
    if (cachedValue != null) {
      log('Found cached value for key: $key');
      return cachedValue;
    }

    try {
      final db = await database;
      final result = await db.query(
        DatabaseConfig.tableKeychain,
        columns: [DatabaseConfig.columnValue],
        where: '${DatabaseConfig.columnKey} = ?',
        whereArgs: [key],
      );

      if (result.isNotEmpty) {
        final value = result.first[DatabaseConfig.columnValue];
        CacheUtil().set(key, value);
        log('Value for key $key: $value');
        return value;
      }
      log('No value found for key: $key');
      return null;
    } catch (error) {
      log('Error getting value for key $key: $error');
      return null;
    }
  }

  @override
  Future<int> set(
      String key, dynamic value, Function(dynamic) cacheSetter) async {
    log('Setting value for key: $key to $value');
    CacheUtil().set(key, value);
    final db = await database;
    final data = {
      DatabaseConfig.columnKey: key,
      DatabaseConfig.columnValue: value.toString(),
      DatabaseConfig.columnUpdateAt: DateTime.now().toIso8601String(),
      DatabaseConfig.columnCreatedAt: DateTime.now().toIso8601String(),
    };

    final result = await db.insert(
      DatabaseConfig.tableKeychain,
      data,
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
    log('Value $value set for key: $key with result: $result');
    return result;
  }

  @override
  Future<int> delete(String key) async {
    log('Deleting value for key: $key');
    final db = await database;
    final result = await db.transaction((txn) async {
      return await txn.delete(
        DatabaseConfig.tableKeychain,
        where: '${DatabaseConfig.columnKey} = ?',
        whereArgs: [key],
      );
    });
    log('Value deleted for key: $key');
    return result;
  }

  @override
  Future<void> purge() async {
    log('Purging SQLite storage');
    await deleteDatabaseDB();
    await database;
    log('Database purged');
  }

  Future<void> deleteDatabaseDB() async {
    log('Deleting database');
    final path = await _getDatabasePath();
    await deleteDatabase(path);
    log('Database deleted');
    _database = null;
  }
}

class SqliteStorageDelegate {}
