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 映射
/// - 持久化决策由调用方决定
///
/// ## 数据流
/// ```
/// 网络：User.fromJson(json) → (可选) saveUser(user) → StorageSdkApi → DB
/// 监听：StorageSdkApi.watchWhere/watchAll → DriftUser → _toEntity() → UI
/// 部分更新：updateFields(uid, UsersCompanion) → StorageSdkApi.updateWhere
/// ```
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 ────────────────────────────────────────────────

  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),
  );

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

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

  /// 监听指定 uid 列表
  @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> saveUser(User user) async {
    await _storage.insertOrReplace<DriftUser>(_toCompanion(user));
  }

  @override
  Future<void> saveUsers(List<User> users) async {
    await _storage.batchInsertOrReplace<DriftUser>(
      users.map(_toCompanion).toList(),
    );
  }

  /// 仅更新指定列，其他列不变
  ///
  /// 示例：
  /// ```dart
  /// await userRepo.updateFields(uid, UsersCompanion(
  ///   nickname: Value('New Name'),
  ///   lastOnline: Value(DateTime.now().millisecondsSinceEpoch),
  /// ));
  /// ```
  @override
  Future<void> updateFields(int uid, UsersCompanion companion) async {
    await _storage.updateWhere<DriftUser, $UsersTable>(
      companion,
      (t) => t.uid.equals(uid),
    );
  }

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

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