import 'dart:convert';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:uuid/uuid.dart';
import '../models/models.dart';

class LocalStorage {
  static const _dbName = 'add_months.db';
  static const _tableName = 'user_data';
  static const _savedRunsTable = 'saved_runs';
  static const _version = 2;

  static Database? _database;
  static const _uuid = Uuid();

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

  static Future<Database> _initDatabase() async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, _dbName);

    return await openDatabase(
      path,
      version: _version,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE $_tableName (
            key TEXT PRIMARY KEY,
            value TEXT NOT NULL,
            updated_at INTEGER NOT NULL
          )
        ''');
        await db.execute('''
          CREATE TABLE $_savedRunsTable (
            id TEXT PRIMARY KEY,
            label TEXT NOT NULL,
            result TEXT NOT NULL,
            profile TEXT NOT NULL,
            behaviors TEXT NOT NULL,
            created_at INTEGER NOT NULL
          )
        ''');
      },
      onUpgrade: (db, oldVersion, newVersion) async {
        if (oldVersion < 2) {
          await db.execute('''
            CREATE TABLE $_savedRunsTable (
              id TEXT PRIMARY KEY,
              label TEXT NOT NULL,
              result TEXT NOT NULL,
              profile TEXT NOT NULL,
              behaviors TEXT NOT NULL,
              created_at INTEGER NOT NULL
            )
          ''');
        }
      },
    );
  }

  // Generic key-value operations
  static Future<void> _put(String key, Map<String, dynamic> value) async {
    final db = await database;
    await db.insert(
      _tableName,
      {
        'key': key,
        'value': jsonEncode(value),
        'updated_at': DateTime.now().millisecondsSinceEpoch,
      },
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  static Future<Map<String, dynamic>?> _get(String key) async {
    final db = await database;
    final results = await db.query(
      _tableName,
      where: 'key = ?',
      whereArgs: [key],
    );

    if (results.isEmpty) return null;
    return jsonDecode(results.first['value'] as String) as Map<String, dynamic>;
  }

  // Profile operations
  static Future<void> saveProfile(UserProfile profile) async {
    await _put('profile', profile.toJson());
  }

  static Future<UserProfile?> getProfile() async {
    final json = await _get('profile');
    if (json == null) return null;
    return UserProfile.fromJson(json);
  }

  // Behavioral inputs operations
  static Future<void> saveBehaviors(BehavioralInputs behaviors) async {
    await _put('behaviors', behaviors.toJson());
  }

  static Future<BehavioralInputs?> getBehaviors() async {
    final json = await _get('behaviors');
    if (json == null) return null;
    return BehavioralInputs.fromJson(json);
  }

  // Result operations
  static Future<void> saveResult(CalculationResult result) async {
    await _put('lastResult', result.toJson());
  }

  static Future<CalculationResult?> getLastResult() async {
    final json = await _get('lastResult');
    if (json == null) return null;
    return CalculationResult.fromJson(json);
  }

  // Check if user has completed setup
  static Future<bool> hasCompletedSetup() async {
    final profile = await getProfile();
    final behaviors = await getBehaviors();
    return profile != null && behaviors != null;
  }

  // Delete all data (including saved runs)
  static Future<void> deleteAllData() async {
    final db = await database;
    await db.delete(_tableName);
    await db.delete(_savedRunsTable);
  }

  // Get last updated timestamp
  static Future<DateTime?> getLastUpdated() async {
    final db = await database;
    final results = await db.query(
      _tableName,
      columns: ['updated_at'],
      orderBy: 'updated_at DESC',
      limit: 1,
    );

    if (results.isEmpty) return null;
    return DateTime.fromMillisecondsSinceEpoch(
      results.first['updated_at'] as int,
    );
  }

  // ============================================
  // Saved Runs operations
  // ============================================

  static Future<String> saveSavedRun({
    required String label,
    required CalculationResult result,
    required UserProfile profile,
    required BehavioralInputs behaviors,
  }) async {
    final db = await database;
    final id = _uuid.v4();
    final now = DateTime.now();

    await db.insert(_savedRunsTable, {
      'id': id,
      'label': label,
      'result': jsonEncode(result.toJson()),
      'profile': jsonEncode(profile.toJson()),
      'behaviors': jsonEncode(behaviors.toJson()),
      'created_at': now.millisecondsSinceEpoch,
    });

    return id;
  }

  static Future<List<SavedRun>> getSavedRuns() async {
    final db = await database;
    final results = await db.query(
      _savedRunsTable,
      orderBy: 'created_at DESC',
    );

    return results.map((row) => SavedRun(
      id: row['id'] as String,
      label: row['label'] as String,
      result: CalculationResult.fromJson(
        jsonDecode(row['result'] as String) as Map<String, dynamic>,
      ),
      profile: UserProfile.fromJson(
        jsonDecode(row['profile'] as String) as Map<String, dynamic>,
      ),
      behaviors: BehavioralInputs.fromJson(
        jsonDecode(row['behaviors'] as String) as Map<String, dynamic>,
      ),
      createdAt: DateTime.fromMillisecondsSinceEpoch(row['created_at'] as int),
    )).toList();
  }

  static Future<SavedRun?> getSavedRun(String id) async {
    final db = await database;
    final results = await db.query(
      _savedRunsTable,
      where: 'id = ?',
      whereArgs: [id],
    );

    if (results.isEmpty) return null;

    final row = results.first;
    return SavedRun(
      id: row['id'] as String,
      label: row['label'] as String,
      result: CalculationResult.fromJson(
        jsonDecode(row['result'] as String) as Map<String, dynamic>,
      ),
      profile: UserProfile.fromJson(
        jsonDecode(row['profile'] as String) as Map<String, dynamic>,
      ),
      behaviors: BehavioralInputs.fromJson(
        jsonDecode(row['behaviors'] as String) as Map<String, dynamic>,
      ),
      createdAt: DateTime.fromMillisecondsSinceEpoch(row['created_at'] as int),
    );
  }

  static Future<void> updateSavedRunLabel(String id, String newLabel) async {
    final db = await database;
    await db.update(
      _savedRunsTable,
      {'label': newLabel},
      where: 'id = ?',
      whereArgs: [id],
    );
  }

  static Future<void> deleteSavedRun(String id) async {
    final db = await database;
    await db.delete(
      _savedRunsTable,
      where: 'id = ?',
      whereArgs: [id],
    );
  }

  static Future<void> deleteAllSavedRuns() async {
    final db = await database;
    await db.delete(_savedRunsTable);
  }

  static Future<int> getSavedRunsCount() async {
    final db = await database;
    final result = await db.rawQuery('SELECT COUNT(*) as count FROM $_savedRunsTable');
    return result.first['count'] as int;
  }

  // ============================================
  // Unit preference operations
  // ============================================

  static Future<void> setUseMetricUnits(bool useMetric) async {
    await _put('useMetricUnits', {'value': useMetric});
  }

  static Future<bool> getUseMetricUnits() async {
    final json = await _get('useMetricUnits');
    if (json == null) return false; // Default to imperial (US)
    return json['value'] as bool;
  }

  static Future<void> setSkipForever(bool skip) async {
    await _put('skipForever', {'value': skip});
  }

  static Future<bool> getSkipForever() async {
    final json = await _get('skipForever');
    if (json == null) return false;
    return json['value'] as bool;
  }
}
