import 'package:hive/hive.dart'; import 'schema/display_data.dart'; import 'schema/hidden_specs.dart'; import 'schema/user_data.dart'; import 'schema/gamification.dart'; import 'schema/metadata.dart'; import 'schema/item_type.dart'; export 'schema/display_data.dart'; export 'schema/hidden_specs.dart'; export 'schema/user_data.dart'; export 'schema/gamification.dart'; export 'schema/metadata.dart'; export 'schema/item_type.dart'; part 'sake_item.g.dart'; @HiveType(typeId: 0) class SakeItem extends HiveObject { // --- Root Identity --- @HiveField(0) final String id; // --- New Hierarchical Data (Fields 20+) --- @HiveField(20) DisplayData? _displayData; @HiveField(21) HiddenSpecs? _hiddenSpecs; @HiveField(22) UserData? _userData; @HiveField(23) Gamification? _gamification; @HiveField(24) Metadata? _metadata; @HiveField(25) ItemType? _itemType; // --- Legacy Fields (Deprecated: Kept for Migration) --- @HiveField(1) final String? legacyName; @HiveField(2) final String? legacyBrand; @HiveField(3) final String? legacyPrefecture; @HiveField(4) final String? legacyDescription; @HiveField(5) final String? legacyCatchCopy; @HiveField(6) final List? legacyImagePaths; @HiveField(7) final double? legacySweetnessScore; @HiveField(8) final double? legacyBodyScore; @HiveField(9) final DateTime? legacyCreatedAt; @HiveField(10) final int? legacyConfidenceScore; @HiveField(11) final List? legacyFlavorTags; @HiveField(12) final bool? legacyIsFavorite; @HiveField(13) final Map? legacyTasteStats; @HiveField(14) final bool? legacyIsUserEdited; @HiveField(15) final int? legacyCostPrice; @HiveField(16) final int? legacyManualPrice; @HiveField(17) final double? legacyMarkup; @HiveField(18) final Map? legacyPriceVariants; SakeItem({ required this.id, DisplayData? displayData, HiddenSpecs? hiddenSpecs, UserData? userData, Gamification? gamification, Metadata? metadata, ItemType? itemType, // Legacy params for migration compatibility (optional) this.legacyName, this.legacyBrand, this.legacyPrefecture, this.legacyDescription, this.legacyCatchCopy, this.legacyImagePaths, this.legacySweetnessScore, this.legacyBodyScore, this.legacyCreatedAt, this.legacyConfidenceScore, this.legacyFlavorTags, this.legacyIsFavorite, this.legacyTasteStats, this.legacyIsUserEdited, this.legacyCostPrice, this.legacyManualPrice, this.legacyMarkup, this.legacyPriceVariants, }) { _displayData = displayData; _hiddenSpecs = hiddenSpecs; _userData = userData; _gamification = gamification; _metadata = metadata; _itemType = itemType; } // --- Smart Getters (Auto-Migration / Fallback) --- DisplayData get displayData { if (_displayData != null) return _displayData!; // Fallback: Construct from Legacy return DisplayData( name: legacyName ?? 'Unknown', brewery: legacyBrand ?? 'Unknown', prefecture: legacyPrefecture ?? 'Unknown', catchCopy: legacyCatchCopy, imagePaths: legacyImagePaths ?? [], rating: null, // Legacy didn't have rating explicit in display ); } // Allow setting for UI updates set displayData(DisplayData val) { _displayData = val; save(); // Auto-save on set? Or just update memory. HiveObject usually requires save(). // Better to just update memory here. } HiddenSpecs get hiddenSpecs { if (_hiddenSpecs != null) return _hiddenSpecs!; return HiddenSpecs( description: legacyDescription, tasteStats: legacyTasteStats ?? {}, flavorTags: legacyFlavorTags ?? [], sweetnessScore: legacySweetnessScore, bodyScore: legacyBodyScore, ); } UserData get userData { if (_userData != null) return _userData!; return UserData( isFavorite: legacyIsFavorite ?? false, isUserEdited: legacyIsUserEdited ?? false, price: legacyManualPrice, costPrice: legacyCostPrice, markup: legacyMarkup ?? 3.0, priceVariants: legacyPriceVariants, ); } Gamification get gamification { if (_gamification != null) return _gamification!; return Gamification( ponPoints: 0, // Legacy didn't track ); } Metadata get metadata { if (_metadata != null) return _metadata!; return Metadata( createdAt: legacyCreatedAt ?? DateTime.now(), aiConfidence: legacyConfidenceScore, ); } ItemType get itemType { // Default to 'sake' for existing data return _itemType ?? ItemType.sake; } set itemType(ItemType val) { _itemType = val; } // --- Migration Method --- // Returns true if migration was performed (i.e., was legacy) bool ensureMigrated() { if (_displayData != null) return false; // Already new structure // Create New Objects from Legacy Fields _displayData = displayData; // Uses getter logic _hiddenSpecs = hiddenSpecs; _userData = userData; _gamification = gamification; _metadata = metadata; return true; } // --- CopyWith for Immutable Updates (Backend Compatible) --- SakeItem copyWith({ String? name, String? brand, // Maps to Brewery String? prefecture, String? description, String? catchCopy, List? imagePaths, double? sweetnessScore, double? bodyScore, int? confidenceScore, // Maps to aiConfidence List? flavorTags, bool? isFavorite, Map? tasteStats, bool? isUserEdited, String? memo, int? costPrice, int? manualPrice, // Maps to price double? markup, Map? priceVariants, ItemType? itemType, }) { // Ensure we have current data structures final currentDisplay = displayData; final currentHidden = hiddenSpecs; final currentUser = userData; final currentMeta = metadata; final currentGame = gamification; return SakeItem( id: id, displayData: currentDisplay.copyWith( name: name, brewery: brand, prefecture: prefecture, catchCopy: catchCopy, imagePaths: imagePaths, ), hiddenSpecs: currentHidden.copyWith( description: description, tasteStats: tasteStats, flavorTags: flavorTags, sweetnessScore: sweetnessScore, bodyScore: bodyScore, ), userData: currentUser.copyWith( isFavorite: isFavorite, isUserEdited: isUserEdited, memo: memo, price: manualPrice, costPrice: costPrice, markup: markup, priceVariants: priceVariants, ), gamification: currentGame, metadata: currentMeta.copyWith( aiConfidence: confidenceScore, ), itemType: itemType ?? this.itemType, ); } // Compact JSON for QR ecosystem String toQrJson() { return '{"id":"$id","n":"${displayData.name}","b":"${displayData.brewery}","p":"${displayData.prefecture ?? ""}","s":${hiddenSpecs.sweetnessScore ?? 0},"y":${hiddenSpecs.bodyScore ?? 0},"a":${hiddenSpecs.alcoholContent ?? 0}}'; } }