import 'package:drift/drift.dart';
import 'package:im_app/data/local/drift/app_database.dart';
import 'package:im_app/domain/entities/user.dart';
import 'package:im_app/domain/repositories/user_repository.dart';
import 'package:storage_sdk/storage_sdk.dart';

/// 用户仓储实现
///
/// ## 职责
/// - 所有 DB 操作通过 [StorageSdkApi]，不直接接触 AppDatabase
/// - DriftUser ↔ Domain User 映射
/// - 所有公开接口只接受 Domain 实体，Companion 转换完全内聚在此类
///
/// ## 数据流
/// ```
/// 网络：User.fromJson(json) → (可选) insertOrReplaceUser(user) → StorageSdkApi → DB
/// 监听：StorageSdkApi.watchWhere/watchAll → DriftUser → _toEntity() → UI
/// 更新：updateUser(user) / updateUsersBatch(users) → _toCompanion() → DB
/// ```
class UserRepositoryImpl implements UserRepository {
  final StorageSdkApi _storage;

  UserRepositoryImpl(this._storage);

  // ── DB row → Domain ──────────────────────────────────────────────────────

  User _toEntity(DriftUser row) => User(
    uid: row.uid,
    uuid: row.uuid,
    lastOnline: row.lastOnline,
    profilePic: row.profilePic,
    profilePicGaussian: row.profilePicGaussian,
    nickname: row.nickname,
    depositName: row.depositName,
    hasSetDepositName: row.hasSetDepositName,
    contact: row.contact,
    countryCode: row.countryCode,
    username: row.username,
    role: row.role,
    relationship: row.relationship,
    friendStatus: row.friendStatus,
    bio: row.bio,
    userAlias: row.userAlias,
    requestAt: row.requestAt,
    deletedAt: row.deletedAt,
    email: row.email,
    recoveryEmail: row.recoveryEmail,
    remark: row.remark,
    source: row.source,
    addIndex: row.addIndex,
    incomingSoundId: row.incomingSoundId,
    outgoingSoundId: row.outgoingSoundId,
    notificationSoundId: row.notificationSoundId,
    sendMessageSoundId: row.sendMessageSoundId,
    groupNotificationSoundId: row.groupNotificationSoundId,
    groupTags: row.groupTags,
    friendTags: row.friendTags,
    publicKey: row.publicKey,
    configBits: row.configBits,
    hint: row.hint,
  );

  // ── Domain → DB companion (internal only) ────────────────────────────────

  UsersCompanion _toCompanion(User user) => UsersCompanion(
    uid: Value(user.uid),
    uuid: Value(user.uuid),
    lastOnline: Value(user.lastOnline),
    profilePic: Value(user.profilePic),
    profilePicGaussian: Value(user.profilePicGaussian ?? ''),
    nickname: Value(user.nickname),
    depositName: Value(user.depositName),
    hasSetDepositName: Value(user.hasSetDepositName ?? 0),
    contact: Value(user.contact),
    countryCode: Value(user.countryCode),
    username: Value(user.username),
    role: Value(user.role),
    relationship: Value(user.relationship),
    friendStatus: Value(user.friendStatus),
    bio: Value(user.bio),
    userAlias: Value(user.userAlias),
    requestAt: Value(user.requestAt),
    deletedAt: Value(user.deletedAt),
    email: Value(user.email),
    recoveryEmail: Value(user.recoveryEmail),
    remark: Value(user.remark),
    source: Value(user.source),
    addIndex: Value(user.addIndex),
    incomingSoundId: Value(user.incomingSoundId ?? 0),
    outgoingSoundId: Value(user.outgoingSoundId ?? 0),
    notificationSoundId: Value(user.notificationSoundId ?? 0),
    sendMessageSoundId: Value(user.sendMessageSoundId ?? 0),
    groupNotificationSoundId: Value(user.groupNotificationSoundId ?? 0),
    groupTags: Value(user.groupTags ?? '[]'),
    friendTags: Value(user.friendTags ?? '[]'),
    publicKey: Value(user.publicKey),
    configBits: Value(user.configBits ?? 0),
    hint: Value(user.hint),
  );

  UsersCompanion _toPartialCompanion(User user) => UsersCompanion(
    uid: Value(user.uid),
    uuid: user.uuid != null ? Value(user.uuid) : const Value.absent(),
    lastOnline: user.lastOnline != null
        ? Value(user.lastOnline)
        : const Value.absent(),
    profilePic: user.profilePic != null
        ? Value(user.profilePic)
        : const Value.absent(),
    profilePicGaussian: user.profilePicGaussian != null
        ? Value(user.profilePicGaussian!)
        : const Value.absent(),
    nickname: user.nickname != null
        ? Value(user.nickname)
        : const Value.absent(),
    depositName: user.depositName != null
        ? Value(user.depositName)
        : const Value.absent(),
    hasSetDepositName: user.hasSetDepositName != null
        ? Value(user.hasSetDepositName!)
        : const Value.absent(),
    contact: user.contact != null ? Value(user.contact) : const Value.absent(),
    countryCode: user.countryCode != null
        ? Value(user.countryCode)
        : const Value.absent(),
    username: user.username != null
        ? Value(user.username)
        : const Value.absent(),
    role: user.role != null ? Value(user.role) : const Value.absent(),
    relationship: user.relationship != null
        ? Value(user.relationship)
        : const Value.absent(),
    friendStatus: user.friendStatus != null
        ? Value(user.friendStatus)
        : const Value.absent(),
    bio: user.bio != null ? Value(user.bio) : const Value.absent(),
    userAlias: user.userAlias != null
        ? Value(user.userAlias)
        : const Value.absent(),
    requestAt: user.requestAt != null
        ? Value(user.requestAt)
        : const Value.absent(),
    deletedAt: user.deletedAt != null
        ? Value(user.deletedAt)
        : const Value.absent(),
    email: user.email != null ? Value(user.email) : const Value.absent(),
    recoveryEmail: user.recoveryEmail != null
        ? Value(user.recoveryEmail)
        : const Value.absent(),
    remark: user.remark != null ? Value(user.remark) : const Value.absent(),
    source: user.source != null ? Value(user.source) : const Value.absent(),
    addIndex: user.addIndex != null
        ? Value(user.addIndex)
        : const Value.absent(),
    incomingSoundId: user.incomingSoundId != null
        ? Value(user.incomingSoundId!)
        : const Value.absent(),
    outgoingSoundId: user.outgoingSoundId != null
        ? Value(user.outgoingSoundId!)
        : const Value.absent(),
    notificationSoundId: user.notificationSoundId != null
        ? Value(user.notificationSoundId!)
        : const Value.absent(),
    sendMessageSoundId: user.sendMessageSoundId != null
        ? Value(user.sendMessageSoundId!)
        : const Value.absent(),
    groupNotificationSoundId: user.groupNotificationSoundId != null
        ? Value(user.groupNotificationSoundId!)
        : const Value.absent(),
    groupTags: user.groupTags != null
        ? Value(user.groupTags!)
        : const Value.absent(),
    friendTags: user.friendTags != null
        ? Value(user.friendTags!)
        : const Value.absent(),
    publicKey: user.publicKey != null
        ? Value(user.publicKey)
        : const Value.absent(),
    configBits: user.configBits != null
        ? Value(user.configBits!)
        : const Value.absent(),
    hint: user.hint != null ? Value(user.hint) : const Value.absent(),
  );

  // ── 监听 ─────────────────────────────────────────────────────────────────

  @override
  Stream<User?> watchUser(int uid) {
    return _storage
        .watchFirst<DriftUser, $UsersTable>((t) => t.uid.equals(uid))
        .map((row) => row != null ? _toEntity(row) : null);
  }

  @override
  Stream<List<User>> watchUsers(List<int> uids) {
    return _storage
        .watchWhere<DriftUser, $UsersTable>((t) => t.uid.isIn(uids))
        .map((rows) => rows.map(_toEntity).toList());
  }

  @override
  Stream<List<User>> watchAllUsers() {
    return _storage.watchAll<DriftUser>().map(
      (rows) => rows.map(_toEntity).toList(),
    );
  }

  // ── 读取 ─────────────────────────────────────────────────────────────────

  @override
  Future<User?> getUser(int uid) async {
    final row = await _storage.selectFirst<DriftUser, $UsersTable>(
      (t) => t.uid.equals(uid),
    );
    return row != null ? _toEntity(row) : null;
  }

  @override
  Future<List<User>> getAllUsers() async {
    final rows = await _storage.selectAll<DriftUser>();
    return rows.map(_toEntity).toList();
  }

  @override
  Future<List<User>> getUsers({required int offset, required int limit}) async {
    final rows = await _storage.rawQuery(
      'SELECT * FROM user ORDER BY id LIMIT ? OFFSET ?',
      [limit, offset],
    );
    return rows
        .map(
          (row) => User(
            uid: row.read<int>('uid'),
            uuid: row.readNullable<String>('uuid'),
            lastOnline: row.readNullable<int>('last_online'),
            profilePic: row.readNullable<String>('profile_pic'),
            profilePicGaussian: row.readNullable<String>(
              'profile_pic_gaussian',
            ),
            nickname: row.readNullable<String>('nickname'),
            depositName: row.readNullable<String>('deposit_name'),
            hasSetDepositName: row.readNullable<int>('has_set_deposit_name'),
            contact: row.readNullable<String>('contact'),
            countryCode: row.readNullable<String>('country_code'),
            username: row.readNullable<String>('username'),
            role: row.readNullable<int>('role'),
            relationship: row.readNullable<int>('relationship'),
            friendStatus: row.readNullable<int>('friend_status'),
            bio: row.readNullable<String>('bio'),
            userAlias: row.readNullable<String>('user_alias'),
            requestAt: row.readNullable<int>('request_at'),
            deletedAt: row.readNullable<int>('deleted_at'),
            email: row.readNullable<String>('email'),
            recoveryEmail: row.readNullable<String>('recovery_email'),
            remark: row.readNullable<String>('remark'),
            source: row.readNullable<String>('source'),
            addIndex: row.readNullable<int>('add_index'),
            incomingSoundId: row.readNullable<int>('incoming_sound_id'),
            outgoingSoundId: row.readNullable<int>('outgoing_sound_id'),
            notificationSoundId: row.readNullable<int>('notification_sound_id'),
            sendMessageSoundId: row.readNullable<int>('send_message_sound_id'),
            groupNotificationSoundId: row.readNullable<int>(
              'group_notification_sound_id',
            ),
            groupTags: row.readNullable<String>('group_tags'),
            friendTags: row.readNullable<String>('friend_tags'),
            publicKey: row.readNullable<String>('public_key'),
            configBits: row.readNullable<int>('config_bits'),
            hint: row.readNullable<String>('hint'),
          ),
        )
        .toList();
  }

  @override
  Future<int> countUsers() async {
    return _storage.count<DriftUser, $UsersTable>();
  }

  // ── 写入 ─────────────────────────────────────────────────────────────────

  @override
  Future<void> insertOrReplaceUser(User user) async {
    if (user.requireUpsert) {
      await upsertUser(user);
    }

    await _storage.insertOrReplace<DriftUser>(_toCompanion(user));
  }

  @override
  Future<void> insertOrReplaceUsers(List<User> users) async {
    final List<User> upsertList = [];
    final List<User> insertList = [];
    for (final user in users) {
      user.requireUpsert ? upsertList.add(user) : insertList.add(user);
    }

    if (insertList.isNotEmpty) {
      await _storage.batchInsertOrReplace<DriftUser>(
        insertList.map(_toCompanion).toList(),
      );
    }
    if (upsertList.isNotEmpty) {
      await upsertUsers(upsertList);
    }
  }

  @override
  Future<void> updateUser(User user) async {
    await _storage.updateWhere<DriftUser, $UsersTable>(
      _toCompanion(user),
      (t) => t.uid.equals(user.uid),
    );
  }

  /// 批量更新已存在的用户记录。
  ///
  /// 仅更新，不插入——调用方应确保这些用户已存在于 DB 中。
  /// 如需 upsert 语义，请使用 [upsertUsers]。
  ///
  /// 所有更新在同一事务内完成，保证原子性。
  @override
  Future<void> updateUsersBatch(List<User> users) async {
    await _storage.transaction(() async {
      for (final user in users) {
        await updateUser(user);
      }
    });
  }

  /// 单条 upsert：不存在则插入，存在则仅更新非 null 字段。
  ///
  /// 内部委托给 [upsertUsers]。
  @override
  Future<void> upsertUser(User user) async {
    await _storage.insertOnConflictUpdate<DriftUser>(_toPartialCompanion(user));
  }

  /// 批量 upsert：不存在则插入，存在则仅更新非 null 字段。
  ///
  /// 与 [insertOrReplaceUsers] 的区别：
  /// - [insertOrReplaceUsers] → INSERT OR REPLACE，全字段覆盖
  /// - [upsertUsers] → INSERT OR IGNORE + UPDATE，仅更新传入的非 null 字段
  ///
  /// 所有操作在同一事务内完成，保证原子性。
  /// 适用场景：从服务端收到部分用户信息，不希望覆盖本地已有的其他字段。
  @override
  Future<void> upsertUsers(List<User> users) async {
    await _storage.batchInsertOnConflictUpdate<DriftUser>(
      users.map(_toPartialCompanion).toList(),
    );
  }

  // ── 删除 ─────────────────────────────────────────────────────────────────

  @override
  Future<void> deleteUser(int uid) async {
    await _storage.deleteWhere<DriftUser, $UsersTable>(
      (t) => t.uid.equals(uid),
    );
  }
}
