class ApiResponse<T> {
  const ApiResponse({required this.code, required this.message, this.data});

  final int code;
  final String message;
  final T? data;

  bool get isSuccess => code == 0 || code >= 200 && code < 300;

  factory ApiResponse.fromJson(
    Map<String, dynamic> json, {
    T Function(dynamic json)? fromJsonT,
  }) {
    final rawData = json['data'];
    T? data;
    if (rawData != null && fromJsonT != null) {
      data = fromJsonT(rawData);
    } else if (rawData != null && fromJsonT == null) {
      // 无 parser 时仅在类型已匹配时接收，避免 Map 被强转为业务模型。
      if (rawData is T) {
        data = rawData;
      } else {
        throw FormatException(
          'ApiResponse data 类型不匹配，且未提供 fromJsonT；'
          '期望 $T，实际 ${rawData.runtimeType}',
        );
      }
    }

    return ApiResponse<T>(
      code: (json['code'] as num?)?.toInt() ?? 200,
      message: (json['message'] ?? json['msg'] ?? '').toString(),
      data: data,
    );
  }
}

class PageResponse<T> {
  const PageResponse({
    required this.items,
    required this.total,
    this.page,
    this.pageSize,
  });

  final List<T> items;
  final int total;
  final int? page;
  final int? pageSize;

  bool get isEmpty => items.isEmpty;

  factory PageResponse.fromJson(
    Map<String, dynamic> json, {
    required T Function(dynamic json) fromJsonT,
  }) {
    final rawItems = json['items'] ?? json['rows'] ?? json['list'] ?? const [];
    return PageResponse<T>(
      items: rawItems is List ? rawItems.map(fromJsonT).toList() : <T>[],
      total: (json['total'] as num?)?.toInt() ?? 0,
      page: (json['page'] as num?)?.toInt(),
      pageSize: (json['pageSize'] as num?)?.toInt(),
    );
  }
}
