import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; part 'container_model.freezed.dart'; part 'container_model.g.dart'; /// Container data model for API serialization. @freezed class ContainerModel with _$ContainerModel { const factory ContainerModel({ @JsonKey(name: 'Id') required String id, @JsonKey(name: 'Names') required List names, @JsonKey(name: 'Image') required String image, @JsonKey(name: 'State') required String state, @JsonKey(name: 'Status') required String status, @JsonKey(name: 'Labels') @Default({}) Map labels, @JsonKey(name: 'Ports') @Default([]) List ports, @JsonKey(name: 'Mounts') @Default([]) List mounts, @JsonKey(name: 'NetworkSettings') NetworkSettingsModel? networkSettings, @JsonKey(name: 'Created') int? created, @JsonKey(name: 'SizeRw') int? sizeRw, @JsonKey(name: 'SizeRootFs') int? sizeRootFs, }) = _ContainerModel; const ContainerModel._(); factory ContainerModel.fromJson(Map json) => _$ContainerModelFromJson(json); /// Converts to domain entity. Container toEntity() { // Extract stack name from labels (Docker Compose convention) final stackName = labels['com.docker.compose.project']; final stackId = stackName; // Use project name as ID for now return Container( id: id.substring(0, 12), fullId: id, name: names.isNotEmpty ? names.first.replaceFirst('/', '') : id, image: image, state: _parseState(state), status: status, stackName: stackName, stackId: stackId, ports: ports.map((p) => p.toEntity()).toList(), labels: labels, mounts: mounts.map((m) => m.toEntity()).toList(), networks: networkSettings?.networks.keys.toList() ?? [], createdAt: created != null ? DateTime.fromMillisecondsSinceEpoch(created! * 1000) : null, ); } ContainerState _parseState(String state) { return switch (state.toLowerCase()) { 'created' => ContainerState.created, 'running' => ContainerState.running, 'paused' => ContainerState.paused, 'restarting' => ContainerState.restarting, 'removing' => ContainerState.removing, 'exited' => ContainerState.exited, 'dead' => ContainerState.dead, _ => ContainerState.exited, }; } } /// Port mapping model. @freezed class PortModel with _$PortModel { const factory PortModel({ @JsonKey(name: 'IP') String? ip, @JsonKey(name: 'PrivatePort') required int privatePort, @JsonKey(name: 'PublicPort') int? publicPort, @JsonKey(name: 'Type') @Default('tcp') String type, }) = _PortModel; const PortModel._(); factory PortModel.fromJson(Map json) => _$PortModelFromJson(json); PortMapping toEntity() { return PortMapping( hostIp: ip, hostPort: publicPort, containerPort: privatePort, protocol: type, ); } } /// Mount model. @freezed class MountModel with _$MountModel { const factory MountModel({ @JsonKey(name: 'Type') required String type, @JsonKey(name: 'Source') required String source, @JsonKey(name: 'Destination') required String destination, @JsonKey(name: 'Mode') @Default('rw') String mode, @JsonKey(name: 'RW') @Default(true) bool rw, }) = _MountModel; const MountModel._(); factory MountModel.fromJson(Map json) => _$MountModelFromJson(json); VolumeMount toEntity() { return VolumeMount( type: type, source: source, destination: destination, mode: rw ? 'rw' : 'ro', ); } } /// Network settings model. @freezed class NetworkSettingsModel with _$NetworkSettingsModel { const factory NetworkSettingsModel({ @JsonKey(name: 'Networks') @Default({}) Map networks, }) = _NetworkSettingsModel; factory NetworkSettingsModel.fromJson(Map json) => _$NetworkSettingsModelFromJson(json); }