// ignore_for_file: sort_constructors_first import 'package:flutter/foundation.dart'; @immutable class PersonProfile { const PersonProfile({ required this.id, required this.name, required this.relationship, required this.affinityScore, required this.nextMoment, required this.tags, required this.notes, }); final String id; final String name; final String relationship; final int affinityScore; final DateTime nextMoment; final List tags; final String notes; PersonProfile copyWith({ String? id, String? name, String? relationship, int? affinityScore, DateTime? nextMoment, List? tags, String? notes, }) { return PersonProfile( id: id ?? this.id, name: name ?? this.name, relationship: relationship ?? this.relationship, affinityScore: affinityScore ?? this.affinityScore, nextMoment: nextMoment ?? this.nextMoment, tags: tags ?? this.tags, notes: notes ?? this.notes, ); } Map toJson() { return { 'id': id, 'name': name, 'relationship': relationship, 'affinityScore': affinityScore, 'nextMoment': nextMoment.toUtc().toIso8601String(), 'tags': tags, 'notes': notes, }; } factory PersonProfile.fromJson(Map json) { return PersonProfile( id: json['id'] as String, name: json['name'] as String, relationship: json['relationship'] as String, affinityScore: (json['affinityScore'] as num).toInt(), nextMoment: DateTime.parse(json['nextMoment'] as String).toLocal(), tags: (json['tags'] as List) .map((dynamic tag) => '$tag') .toList(), notes: json['notes'] as String, ); } } @immutable class RelationshipMoment { const RelationshipMoment({ required this.id, required this.personId, required this.title, required this.summary, required this.at, required this.type, }); final String id; final String personId; final String title; final String summary; final DateTime at; final String type; RelationshipMoment copyWith({ String? id, String? personId, String? title, String? summary, DateTime? at, String? type, }) { return RelationshipMoment( id: id ?? this.id, personId: personId ?? this.personId, title: title ?? this.title, summary: summary ?? this.summary, at: at ?? this.at, type: type ?? this.type, ); } Map toJson() { return { 'id': id, 'personId': personId, 'title': title, 'summary': summary, 'at': at.toUtc().toIso8601String(), 'type': type, }; } factory RelationshipMoment.fromJson(Map json) { return RelationshipMoment( id: json['id'] as String, personId: json['personId'] as String, title: json['title'] as String, summary: json['summary'] as String, at: DateTime.parse(json['at'] as String).toLocal(), type: json['type'] as String, ); } } @immutable class DashboardTask { const DashboardTask({ required this.id, required this.title, required this.description, required this.when, this.done = false, }); final String id; final String title; final String description; final DateTime when; final bool done; DashboardTask copyWith({ String? id, String? title, String? description, DateTime? when, bool? done, }) { return DashboardTask( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, when: when ?? this.when, done: done ?? this.done, ); } Map toJson() { return { 'id': id, 'title': title, 'description': description, 'when': when.toUtc().toIso8601String(), 'done': done, }; } factory DashboardTask.fromJson(Map json) { return DashboardTask( id: json['id'] as String, title: json['title'] as String, description: json['description'] as String, when: DateTime.parse(json['when'] as String).toLocal(), done: json['done'] as bool? ?? false, ); } } @immutable class DashboardSummary { const DashboardSummary({ required this.activePeople, required this.upcomingPlans, required this.pendingIdeas, required this.weeklyConsistency, }); final int activePeople; final int upcomingPlans; final int pendingIdeas; final int weeklyConsistency; } @immutable class LocalDataState { const LocalDataState({ required this.people, required this.moments, required this.tasks, }); final List people; final List moments; final List tasks; LocalDataState copyWith({ List? people, List? moments, List? tasks, }) { return LocalDataState( people: people ?? this.people, moments: moments ?? this.moments, tasks: tasks ?? this.tasks, ); } DashboardSummary summary({DateTime? now}) { final DateTime baseline = now ?? DateTime.now(); return DashboardSummary( activePeople: people.length, upcomingPlans: people .where((PersonProfile p) => p.nextMoment.isAfter(baseline)) .length, pendingIdeas: people.fold( 0, (int acc, PersonProfile person) => acc + (person.tags.isNotEmpty ? 1 : 0), ), weeklyConsistency: moments.isEmpty ? 0 : (70 + moments.length * 4).clamp(0, 100), ); } Map toJson() { return { 'people': people .map((PersonProfile person) => person.toJson()) .toList(growable: false), 'moments': moments .map((RelationshipMoment moment) => moment.toJson()) .toList(growable: false), 'tasks': tasks .map((DashboardTask task) => task.toJson()) .toList(growable: false), }; } factory LocalDataState.fromJson(Map json) { return LocalDataState( people: (json['people'] as List) .map( (dynamic person) => PersonProfile.fromJson(person as Map), ) .toList(growable: false), moments: (json['moments'] as List) .map( (dynamic moment) => RelationshipMoment.fromJson(moment as Map), ) .toList(growable: false), tasks: (json['tasks'] as List) .map( (dynamic task) => DashboardTask.fromJson(task as Map), ) .toList(growable: false), ); } static LocalDataState seed() { return LocalDataState( people: [ PersonProfile( id: 'p-ava', name: 'Ava Hart', relationship: 'Partner', affinityScore: 92, nextMoment: DateTime(2026, 2, 16, 19, 30), tags: ['coffee', 'books', 'slow mornings'], notes: 'Prefers thoughtful plans over expensive plans.', ), PersonProfile( id: 'p-jordan', name: 'Jordan Lee', relationship: 'Close Friend', affinityScore: 78, nextMoment: DateTime(2026, 2, 18, 18, 0), tags: ['running', 'street food'], notes: 'Has a race on Sunday; ask how training is going.', ), PersonProfile( id: 'p-mila', name: 'Mila Stone', relationship: 'Sister', affinityScore: 84, nextMoment: DateTime(2026, 2, 20, 12, 0), tags: ['plants', 'retro music'], notes: 'Birthday prep should start this week.', ), ], moments: [ RelationshipMoment( id: 'm-1', personId: 'p-ava', title: 'Sunday Walk Tradition', summary: 'Short walk + no-phone hour worked really well.', at: DateTime(2026, 2, 9, 9, 30), type: 'ritual', ), RelationshipMoment( id: 'm-2', personId: 'p-jordan', title: 'Post-workout Check-in', summary: 'Shared training playlist and grabbed smoothies.', at: DateTime(2026, 2, 11, 19, 0), type: 'support', ), RelationshipMoment( id: 'm-3', personId: 'p-mila', title: 'Gift Idea Captured', summary: 'Found a vintage lamp from her saved style board.', at: DateTime(2026, 2, 12, 14, 20), type: 'gift', ), ], tasks: [ DashboardTask( id: 't-1', title: 'Plan Friday dinner with Ava', description: 'Keep it low-key: ramen + bookstore stop.', when: DateTime(2026, 2, 16, 17, 0), ), DashboardTask( id: 't-2', title: 'Send race-day encouragement to Jordan', description: 'Voice note before 8:00 AM.', when: DateTime(2026, 2, 17, 7, 30), ), DashboardTask( id: 't-3', title: 'Finalize Mila birthday shortlist', description: 'Pick 1 meaningful gift and 1 backup.', when: DateTime(2026, 2, 18, 20, 0), ), ], ); } }