// ignore_for_file: sort_constructors_first import 'package:flutter/foundation.dart'; enum AiSuggestionKind { giftIdea, eventIdea, checkIn, reminder } enum AiSuggestionStatus { pending, accepted, dismissed } enum LlmDigestCadence { weekly } @immutable class AiSuggestionDraft { const AiSuggestionDraft({ required this.id, required this.personId, required this.kind, required this.title, required this.details, required this.suggestedAt, required this.confidence, required this.status, required this.sourceRunId, this.suggestedFor, this.reason, }); final String id; final String personId; final AiSuggestionKind kind; final String title; final String details; final DateTime suggestedAt; final DateTime? suggestedFor; final double confidence; final AiSuggestionStatus status; final String sourceRunId; final String? reason; AiSuggestionDraft copyWith({ String? id, String? personId, AiSuggestionKind? kind, String? title, String? details, DateTime? suggestedAt, DateTime? suggestedFor, double? confidence, AiSuggestionStatus? status, String? sourceRunId, String? reason, }) { return AiSuggestionDraft( id: id ?? this.id, personId: personId ?? this.personId, kind: kind ?? this.kind, title: title ?? this.title, details: details ?? this.details, suggestedAt: suggestedAt ?? this.suggestedAt, suggestedFor: suggestedFor ?? this.suggestedFor, confidence: confidence ?? this.confidence, status: status ?? this.status, sourceRunId: sourceRunId ?? this.sourceRunId, reason: reason ?? this.reason, ); } Map toJson() { return { 'id': id, 'personId': personId, 'kind': kind.name, 'title': title, 'details': details, 'suggestedAt': suggestedAt.toUtc().toIso8601String(), 'suggestedFor': suggestedFor?.toUtc().toIso8601String(), 'confidence': confidence, 'status': status.name, 'sourceRunId': sourceRunId, 'reason': reason, }; } factory AiSuggestionDraft.fromJson(Map json) { final String kindName = json['kind'] as String? ?? AiSuggestionKind.checkIn.name; final String statusName = json['status'] as String? ?? AiSuggestionStatus.pending.name; return AiSuggestionDraft( id: json['id'] as String, personId: json['personId'] as String, kind: AiSuggestionKind.values.firstWhere( (AiSuggestionKind value) => value.name == kindName, orElse: () => AiSuggestionKind.checkIn, ), title: json['title'] as String? ?? 'Suggestion', details: json['details'] as String? ?? '', suggestedAt: DateTime.parse( json['suggestedAt'] as String? ?? DateTime.now().toUtc().toIso8601String(), ).toLocal(), suggestedFor: json['suggestedFor'] == null ? null : DateTime.parse(json['suggestedFor'] as String).toLocal(), confidence: (json['confidence'] as num?)?.toDouble() ?? 0, status: AiSuggestionStatus.values.firstWhere( (AiSuggestionStatus value) => value.name == statusName, orElse: () => AiSuggestionStatus.pending, ), sourceRunId: json['sourceRunId'] as String? ?? 'unknown-run', reason: json['reason'] as String?, ); } } @immutable class LlmDigestRunState { const LlmDigestRunState({ this.lastStartedAt, this.lastCompletedAt, this.lastFailure, this.consecutiveFailures = 0, this.running = false, }); final DateTime? lastStartedAt; final DateTime? lastCompletedAt; final String? lastFailure; final int consecutiveFailures; final bool running; LlmDigestRunState copyWith({ DateTime? lastStartedAt, DateTime? lastCompletedAt, String? lastFailure, int? consecutiveFailures, bool? running, }) { return LlmDigestRunState( lastStartedAt: lastStartedAt ?? this.lastStartedAt, lastCompletedAt: lastCompletedAt ?? this.lastCompletedAt, lastFailure: lastFailure, consecutiveFailures: consecutiveFailures ?? this.consecutiveFailures, running: running ?? this.running, ); } Map toJson() { return { 'lastStartedAt': lastStartedAt?.toUtc().toIso8601String(), 'lastCompletedAt': lastCompletedAt?.toUtc().toIso8601String(), 'lastFailure': lastFailure, 'consecutiveFailures': consecutiveFailures, 'running': running, }; } factory LlmDigestRunState.fromJson(Map json) { return LlmDigestRunState( lastStartedAt: json['lastStartedAt'] == null ? null : DateTime.parse(json['lastStartedAt'] as String).toLocal(), lastCompletedAt: json['lastCompletedAt'] == null ? null : DateTime.parse(json['lastCompletedAt'] as String).toLocal(), lastFailure: json['lastFailure'] as String?, consecutiveFailures: (json['consecutiveFailures'] as num?)?.toInt() ?? 0, running: json['running'] as bool? ?? false, ); } } @immutable class LlmDigestConfigState { const LlmDigestConfigState({ this.enabled = false, this.cadence = LlmDigestCadence.weekly, this.preferredWeekday = DateTime.sunday, this.preferredHour = 21, this.requireWifi = true, this.requireCharging = true, }); final bool enabled; final LlmDigestCadence cadence; final int preferredWeekday; final int preferredHour; final bool requireWifi; final bool requireCharging; LlmDigestConfigState copyWith({ bool? enabled, LlmDigestCadence? cadence, int? preferredWeekday, int? preferredHour, bool? requireWifi, bool? requireCharging, }) { return LlmDigestConfigState( enabled: enabled ?? this.enabled, cadence: cadence ?? this.cadence, preferredWeekday: preferredWeekday ?? this.preferredWeekday, preferredHour: preferredHour ?? this.preferredHour, requireWifi: requireWifi ?? this.requireWifi, requireCharging: requireCharging ?? this.requireCharging, ); } }