import 'package:sqflite/sqflite.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:chepuhagram/data/models/message_model.dart';

class LocalDbService {
  static final LocalDbService _instance = LocalDbService._internal();
  static Database? _database;

  factory LocalDbService() => _instance;
  LocalDbService._internal();

  static const int _dbVersion = 8;

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

  Future<void> _createMessagesTable(Database db) async {
    await db.execute('''
      CREATE TABLE messages(
        id INTEGER PRIMARY KEY,
        sender_id INTEGER,
        receiver_id INTEGER,
        content TEXT,
        timestamp TEXT,
        delivered_at TEXT,
        read_at TEXT,
        reply_to_id INTEGER,
        reply_to_text TEXT,
        edited_at TEXT,
        message_type TEXT DEFAULT 'text',
        file_id TEXT,
        encrypted_key TEXT,
        file_name TEXT,
        file_size INTEGER
      )
    ''');
  }

  Future<Database> _initDb() async {
    String path = join(await getDatabasesPath(), 'chat_app.db');
    return await openDatabase(
      path,
      version: _dbVersion,
      onCreate: (db, version) async {
        await _createMessagesTable(db);
      },
      onUpgrade: (db, oldVersion, newVersion) async {
        if (oldVersion < 8) {
          // v8: stop storing media bytes in SQLite; rebuild messages table.
          await db.execute('DROP TABLE IF EXISTS messages');
          await _createMessagesTable(db);
          return;
        }
        if (oldVersion < 2) {
          await db.execute('ALTER TABLE messages ADD COLUMN delivered_at TEXT');
          await db.execute('ALTER TABLE messages ADD COLUMN read_at TEXT');
        }
        if (oldVersion < 3) {
          await db.execute(
            'ALTER TABLE messages ADD COLUMN reply_to_id INTEGER',
          );
          await db.execute(
            'ALTER TABLE messages ADD COLUMN reply_to_text TEXT',
          );
        }
        if (oldVersion < 4) {
          await db.execute('ALTER TABLE messages ADD COLUMN edited_at TEXT');
        }
        if (oldVersion < 5) {
          try {
            await db.execute(
              'ALTER TABLE messages ADD COLUMN message_type TEXT',
            );
          } catch (e) {
            print('message_type column already exists: $e');
          }
          try {
            await db.execute('ALTER TABLE messages ADD COLUMN file_id TEXT');
          } catch (e) {
            print('file_id column already exists: $e');
          }
        }
        if (oldVersion < 6) {
          try {
            await db.execute(
              'ALTER TABLE messages ADD COLUMN encrypted_key TEXT',
            );
          } catch (e) {
            print('encrypted_key column already exists: $e');
          }
        }
        // old migrations kept for safety, but v8 rebuild returns early.
      },
    );
  }

  Future<void> clearDatabase() async {
    final db = await database;
    await db.delete('messages');
  }

  Future<void> saveMessages(List<dynamic> messages) async {
    final db = await database;
    final List<int> incomingIds = messages.map((msg) {
      return (msg is MessageModel) ? msg.id! : (msg['id'] as int);
    }).toList();

    Batch batch = db.batch();

    if (incomingIds.isNotEmpty) {
      batch.delete('messages', where: 'id NOT IN (${incomingIds.join(',')})');
    }
    for (var msg in messages) {
      if (msg is MessageModel) {
        batch.insert('messages', {
          'id': msg.id,
          'sender_id': msg.senderId,
          'receiver_id': msg.receiverId,
          'content': msg.text, 
          'timestamp': msg.createdAt.toIso8601String(),
          'delivered_at': null,
          'read_at': null,
          'reply_to_id': msg.replyToId,
          'reply_to_text': msg.replyToText,
          'edited_at': msg.editedAt?.toIso8601String(),
          'message_type': msg.messageType.name,
          'file_id': msg.fileId,
          'encrypted_key': msg.encryptedFileKey,
          'file_name': msg.fileName,
          'file_size': msg.fileSize,
        }, conflictAlgorithm: ConflictAlgorithm.replace);
      } else {
        // Если это Map из API
        batch.insert('messages', {
          'id': msg['id'],
          'sender_id': msg['sender_id'],
          'receiver_id':
              msg['receiver_id'], // Убедись, что ключ совпадает с API
          'content': msg['content'],
          'timestamp': msg['timestamp'],
          'delivered_at': msg['delivered_at'],
          'read_at': msg['read_at'],
          'reply_to_id': msg['reply_to_id'],
          'reply_to_text': msg['reply_to_text'],
          'edited_at': msg['edited_at'],
          'message_type': msg['message_type'] ?? 'text',
          'file_id': msg['file_id'],
          'encrypted_key': msg['encrypted_key'],
          'file_name': msg['file_name'],
          'file_size': msg['file_size'],
        }, conflictAlgorithm: ConflictAlgorithm.replace);
      }
    }
    await batch.commit(noResult: true);
  }

  // Получение сообщений конкретного чата
  Future<List<Map<String, dynamic>>> getChatHistory(
    int contactId,
    int myId,
  ) async {
    final db = await database;
    return await db.query(
      'messages',
      where:
          '(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)',
      whereArgs: [contactId, myId, myId, contactId],
      orderBy: 'timestamp ASC',
    );
  }

  Future<int> deleteChatHistory(int contactId, int myId) async {
    final db = await database;
    return await db.delete(
      'messages',
      where:
          '(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)',
      whereArgs: [contactId, myId, myId, contactId],
    );
  }

  Future<Map<String, dynamic>?> getLastMessage(int contactId, int myId) async {
    final db = await database;
    final rows = await db.query(
      'messages',
      columns: ['sender_id', 'receiver_id', 'content', 'timestamp'],
      where:
          '(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)',
      whereArgs: [contactId, myId, myId, contactId],
      orderBy: 'timestamp DESC',
      limit: 1,
    );
    if (rows.isEmpty) return null;
    return rows.first;
  }

  Future<void> updateDeliveredAt(int messageId, DateTime deliveredAt) async {
    final db = await database;
    await db.update(
      'messages',
      {'delivered_at': deliveredAt.toIso8601String()},
      where: 'id = ?',
      whereArgs: [messageId],
    );
  }

  Future<void> updateReadAt(int messageId, DateTime readAt) async {
    final db = await database;
    await db.update(
      'messages',
      {'read_at': readAt.toIso8601String()},
      where: 'id = ?',
      whereArgs: [messageId],
    );
  }

  Future<void> updateMessageContent(
    int messageId,
    String content,
    DateTime? editedAt,
  ) async {
    final db = await database;
    await db.update(
      'messages',
      {'content': content, 'edited_at': editedAt?.toIso8601String()},
      where: 'id = ?',
      whereArgs: [messageId],
    );
  }

  Future<void> deleteMessage(int messageId) async {
    final db = await database;
    await db.delete('messages', where: 'id = ?', whereArgs: [messageId]);
  }
}
