import 'package:drift/drift.dart';
import 'package:im_app/data/local/drift/app_database.dart';
import 'package:im_app/domain/entities/api_retry.dart';
import 'package:im_app/domain/enums/retry_status.dart';
import 'package:im_app/domain/repositories/api_retry_repository.dart';
import 'package:storage_sdk/storage_sdk.dart';

class ApiRetryRepositoryImpl implements ApiRetryRepository {
  final StorageSdkApi _storage;

  ApiRetryRepositoryImpl(this._storage);

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

  ApiRetry _toEntity(DriftApiRetry row) => ApiRetry(
    id: row.id,
    uid: row.uid,
    apiType: row.apiType,
    endPoint: row.endPoint,
    requestData: row.requestData,
    synced: row.synced,
    callbackFun: row.callbackFun,
    expired: row.expired,
    replace: row.replace,
    expireTime: row.expireTime,
    createTime: row.createTime,
    addIndex: row.addIndex,
  );

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

  ApiRetriesCompanion _toCompanion(ApiRetry apiRetry) => ApiRetriesCompanion(
    // id is autoIncrement — omit if null so DB generates it
    id: apiRetry.id != null ? Value(apiRetry.id!) : const Value.absent(),
    uid: Value(apiRetry.uid),
    apiType: Value(apiRetry.apiType),
    endPoint: Value(apiRetry.endPoint),
    requestData: Value(apiRetry.requestData),
    synced: Value(apiRetry.synced),
    callbackFun: Value(apiRetry.callbackFun),
    expired: Value(apiRetry.expired),
    replace: Value(apiRetry.replace),
    expireTime: Value(apiRetry.expireTime),
    createTime: Value(apiRetry.createTime),
    addIndex: Value(apiRetry.addIndex),
  );

  // ── rawQuery row → Domain ─────────────────────────────────────────────────

  ApiRetry _fromRow(QueryRow row) => ApiRetry(
    id: row.read<int>('id'),
    uid: row.readNullable<int>('uid'),
    apiType: row.read<String>('api_type'),
    endPoint: row.read<String>('end_point'),
    requestData: row.read<String>('request_data'),
    synced: row.readNullable<int>('synced'),
    callbackFun: row.read<String>('callback_fun'),
    expired: row.readNullable<int>('expired'),
    replace: row.readNullable<int>('replace'),
    expireTime: row.readNullable<int>('expire_time'),
    createTime: row.readNullable<int>('create_time'),
    addIndex: row.readNullable<int>('add_index'),
  );

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

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

  @override
  Stream<List<ApiRetry>> watchPending() {
    return _storage
        .watchWhere<DriftApiRetry, $ApiRetriesTable>((t) => t.synced.equals(0))
        .map((rows) => rows.map(_toEntity).toList());
  }

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

  @override
  Future<List<ApiRetry>> getAll() async {
    final rows = await _storage.selectAll<DriftApiRetry>();
    return rows.map(_toEntity).toList();
  }

  @override
  Future<ApiRetry?> getById(int id) async {
    final row = await _storage.selectFirst<DriftApiRetry, $ApiRetriesTable>(
      (t) => t.id.equals(id),
    );
    return row != null ? _toEntity(row) : null;
  }

  @override
  Future<ApiRetry?> getByUid(int uid) async {
    final row = await _storage.selectFirst<DriftApiRetry, $ApiRetriesTable>(
      (t) => t.uid.equals(uid),
    );
    return row != null ? _toEntity(row) : null;
  }

  @override
  Future<List<ApiRetry>> getPending() async {
    final rows = await _storage.selectWhere<DriftApiRetry, $ApiRetriesTable>(
      (t) => t.synced.equals(0),
    );
    return rows.map(_toEntity).toList();
  }

  @override
  Future<List<ApiRetry>> getActive() async {
    final rows = await _storage.selectWhere<DriftApiRetry, $ApiRetriesTable>(
      (t) => t.expired.equals(0),
    );
    return rows.map(_toEntity).toList();
  }

  @override
  Future<List<ApiRetry>> getByEndPointPending(String endPoint) async {
    final rows = await _storage.rawQuery(
      'SELECT * FROM api_retry WHERE end_point = ? AND synced = 0 ORDER BY create_time ASC',
      [endPoint],
    );
    return rows.map(_fromRow).toList();
  }

  @override
  Future<List<ApiRetry>> getBySynced(int synced) async {
    final rows = await _storage.rawQuery(
      'SELECT * FROM api_retry WHERE synced = ? ORDER BY create_time ASC',
      [synced],
    );
    return rows.map(_fromRow).toList();
  }

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

  @override
  Future<void> insert(ApiRetry apiRetry) async {
    await _storage.insert<DriftApiRetry>(_toCompanion(apiRetry));
  }

  @override
  Future<void> insertOrReplace(ApiRetry apiRetry) async {
    await _storage.insertOrReplace<DriftApiRetry>(_toCompanion(apiRetry));
  }

  @override
  Future<void> update(ApiRetry apiRetry) async {
    if (apiRetry.id == null) return;
    await _storage.updateWhere<DriftApiRetry, $ApiRetriesTable>(
      _toCompanion(apiRetry),
      (t) => t.id.equals(apiRetry.id!),
    );
  }

  @override
  Future<void> markSynced(int id) async {
    await _storage.updateWhere<DriftApiRetry, $ApiRetriesTable>(
      const ApiRetriesCompanion(synced: Value(1)),
      (t) => t.id.equals(id),
    );
  }

  @override
  Future<void> markExpired(int id) async {
    await _storage.updateWhere<DriftApiRetry, $ApiRetriesTable>(
      const ApiRetriesCompanion(expired: Value(1)),
      (t) => t.id.equals(id),
    );
  }

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

  @override
  Future<void> delete(int id) async {
    await _storage.deleteWhere<DriftApiRetry, $ApiRetriesTable>(
      (t) => t.id.equals(id),
    );
  }

  @override
  Future<void> deleteByUid(int uid) async {
    await _storage.deleteWhere<DriftApiRetry, $ApiRetriesTable>(
      (t) => t.uid.equals(uid),
    );
  }

  @override
  Future<void> deleteFinished() async {
    await _storage
        .rawQuery('DELETE FROM api_retry WHERE synced IN (?, ?, ?, ?)', [
          RetryStatus.success.value,
          RetryStatus.failed.value,
          RetryStatus.cancel.value,
          RetryStatus.replace.value,
        ]);
  }

  @override
  Future<void> deleteSynced() async {
    await _storage.deleteWhere<DriftApiRetry, $ApiRetriesTable>(
      (t) => t.synced.equals(1),
    );
  }

  @override
  Future<void> clear() async {
    await _storage.deleteAll<DriftApiRetry>();
  }
}
