class AiModelInfo {
  const AiModelInfo({
    required this.id,
    required this.kind,
    required this.name,
    required this.version,
    required this.size,
    required this.memoryRequired,
    required this.recommendedTier,
    required this.downloaded,
    required this.downloadProgress,
  });

  final String id;
  final String kind;
  final String name;
  final String version;
  final int size;
  final int memoryRequired;
  final String recommendedTier;
  final bool downloaded;
  final int downloadProgress;

  String get kindLabel {
    switch (kind.toLowerCase()) {
      case 'llm':
        return 'LLM';
      case 'asr':
        return 'ASR';
      case 'tts':
        return 'TTS';
      default:
        return kind.toUpperCase();
    }
  }

  String get sizeLabel => _formatBytes(size);
  String get memoryLabel => _formatBytes(memoryRequired);

  factory AiModelInfo.fromJson(Map<String, dynamic> json) {
    return AiModelInfo(
      id: json['id']?.toString() ?? '',
      kind: json['kind']?.toString() ?? '',
      name: json['name']?.toString() ?? json['id']?.toString() ?? '',
      version: json['version']?.toString() ?? '',
      size: _asInt(json['size']),
      memoryRequired: _asInt(json['memory_required']),
      recommendedTier: json['recommended_tier']?.toString() ?? '',
      downloaded: json['downloaded'] == true,
      downloadProgress: _asInt(json['download_progress']),
    );
  }

  static int _asInt(dynamic value) {
    if (value is int) return value;
    if (value is num) return value.toInt();
    return int.tryParse(value?.toString() ?? '') ?? 0;
  }

  static String _formatBytes(int bytes) {
    if (bytes <= 0) return '0 B';
    if (bytes < 1024) return '$bytes B';
    if (bytes < 1024 * 1024) {
      return '${(bytes / 1024).toStringAsFixed(1)} KB';
    }
    if (bytes < 1024 * 1024 * 1024) {
      return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
    }
    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
  }
}

class ModelsSnapshot {
  const ModelsSnapshot({
    required this.models,
    required this.active,
    required this.usedSpace,
    required this.quota,
  });

  final List<AiModelInfo> models;
  final Map<String, String> active;
  final int usedSpace;
  final int quota;

  String get usedLabel => AiModelInfo._formatBytes(usedSpace);
  String get quotaLabel => AiModelInfo._formatBytes(quota);
  double get usageRatio => quota <= 0 ? 0 : (usedSpace / quota).clamp(0.0, 1.0);

  bool isActive(AiModelInfo model) {
    final current = active[model.kind.toLowerCase()];
    return current != null && current == model.id;
  }

  factory ModelsSnapshot.fromJson(Map<String, dynamic> json) {
    final rawModels = json['models'];
    final models = <AiModelInfo>[];
    if (rawModels is List) {
      for (final item in rawModels) {
        if (item is Map<String, dynamic>) {
          models.add(AiModelInfo.fromJson(item));
        } else if (item is Map) {
          models.add(AiModelInfo.fromJson(Map<String, dynamic>.from(item)));
        }
      }
    }

    final active = <String, String>{};
    final rawActive = json['active'];
    if (rawActive is Map) {
      rawActive.forEach((key, value) {
        active[key.toString().toLowerCase()] = value.toString();
      });
    }

    return ModelsSnapshot(
      models: models,
      active: active,
      usedSpace: AiModelInfo._asInt(json['used_space']),
      quota: AiModelInfo._asInt(json['quota']),
    );
  }
}
