import 'divination_params.dart'; import 'divination_result.dart'; class PointsBalanceData { const PointsBalanceData({ required this.balance, required this.frozenBalance, required this.availableBalance, required this.runCost, required this.canRun, }); factory PointsBalanceData.fromJson(Map json) { return PointsBalanceData( balance: _requiredInt(json, 'balance'), frozenBalance: _requiredInt(json, 'frozenBalance'), availableBalance: _requiredInt(json, 'availableBalance'), runCost: _requiredInt(json, 'runCost'), canRun: _requiredBool(json, 'canRun'), ); } final int balance; final int frozenBalance; final int availableBalance; final int runCost; final bool canRun; } class RunAcceptedData { const RunAcceptedData({ required this.taskId, required this.threadId, required this.runId, required this.created, }); factory RunAcceptedData.fromJson(Map json) { return RunAcceptedData( taskId: _requiredString(json, 'taskId'), threadId: _requiredString(json, 'threadId'), runId: _requiredString(json, 'runId'), created: _requiredBool(json, 'created'), ); } final String taskId; final String threadId; final String runId; final bool created; } class DivinationRunAggregate { const DivinationRunAggregate({ required this.derived, required this.signLevel, required this.summary, required this.conclusion, required this.focusPoints, required this.advice, required this.keywords, required this.answer, }); final DerivedDivinationData derived; final String signLevel; final String summary; final List conclusion; final List focusPoints; final List advice; final List keywords; final String answer; DivinationResultData toViewData(DivinationParams params) { return DivinationResultData( params: params, binaryCode: derived.binaryCode, changedBinaryCode: derived.changedBinaryCode, guaName: derived.guaName, targetGuaName: derived.targetGuaName, upperName: derived.upperName, lowerName: derived.lowerName, signType: signLevel, keywords: keywords.join('、'), conclusion: _asBullet(conclusion), analysis: summary.isEmpty ? answer : '$summary\n\n$answer', suggestion: _asBullet(advice), ganzhi: GanzhiData( yearGanZhi: derived.ganzhi.yearGanZhi, monthGanZhi: derived.ganzhi.monthGanZhi, dayGanZhi: derived.ganzhi.dayGanZhi, timeGanZhi: derived.ganzhi.timeGanZhi, yearKongWang: derived.ganzhi.yearKongWang, monthKongWang: derived.ganzhi.monthKongWang, dayKongWang: derived.ganzhi.dayKongWang, timeKongWang: derived.ganzhi.timeKongWang, yueJian: derived.ganzhi.yueJian, riChen: derived.ganzhi.riChen, yuePo: derived.ganzhi.yuePo, riChong: derived.ganzhi.riChong, ), wuXingStatus: derived.wuXingStatuses, yaoLines: derived.yaoInfoList .map((line) => line.toViewModel()) .toList(growable: false), targetYaoLines: derived.targetYaoInfoList .map((line) => line.toViewModel()) .toList(growable: false), ); } String _asBullet(List lines) { if (lines.isEmpty) { return ''; } return List.generate( lines.length, (i) => '${i + 1}. ${lines[i]}', growable: false, ).join('\n'); } } class DerivedDivinationData { const DerivedDivinationData({ required this.question, required this.questionType, required this.divinationMethod, required this.divinationTime, required this.binaryCode, required this.changedBinaryCode, required this.guaName, required this.upperName, required this.lowerName, required this.targetGuaName, required this.worldPosition, required this.responsePosition, required this.hasChangingYao, required this.ganzhi, required this.wuXingStatuses, required this.yaoInfoList, required this.targetYaoInfoList, }); factory DerivedDivinationData.fromJson(Map json) { final wuXingRaw = _requiredMap(json, 'wuXingStatuses'); return DerivedDivinationData( question: _requiredString(json, 'question'), questionType: _requiredString(json, 'questionType'), divinationMethod: _requiredString(json, 'divinationMethod'), divinationTime: _requiredString(json, 'divinationTime'), binaryCode: _requiredString(json, 'binaryCode'), changedBinaryCode: _requiredString(json, 'changedBinaryCode'), guaName: _requiredString(json, 'guaName'), upperName: _requiredString(json, 'upperName'), lowerName: _requiredString(json, 'lowerName'), targetGuaName: _requiredString(json, 'targetGuaName'), worldPosition: _requiredInt(json, 'worldPosition'), responsePosition: _requiredInt(json, 'responsePosition'), hasChangingYao: _requiredBool(json, 'hasChangingYao'), ganzhi: GanzhiBackend.fromJson(_requiredMap(json, 'ganzhi')), wuXingStatuses: wuXingRaw.map( (key, value) => MapEntry(key, value.toString()), ), yaoInfoList: _parseYaoList(json['yaoInfoList']), targetYaoInfoList: _parseYaoList(json['targetYaoInfoList']), ); } final String question; final String questionType; final String divinationMethod; final String divinationTime; final String binaryCode; final String changedBinaryCode; final String guaName; final String upperName; final String lowerName; final String targetGuaName; final int worldPosition; final int responsePosition; final bool hasChangingYao; final GanzhiBackend ganzhi; final Map wuXingStatuses; final List yaoInfoList; final List targetYaoInfoList; static List _parseYaoList(Object? raw) { final list = raw as List?; if (list == null) { throw const FormatException( 'Missing required list: yaoInfoList/targetYaoInfoList', ); } return list .map((item) { if (item is! Map) { throw const FormatException('Invalid yao line item'); } return YaoBackendLine.fromJson(item); }) .toList(growable: false); } } class GanzhiBackend { const GanzhiBackend({ required this.yearGanZhi, required this.monthGanZhi, required this.dayGanZhi, required this.timeGanZhi, required this.yearKongWang, required this.monthKongWang, required this.dayKongWang, required this.timeKongWang, required this.yueJian, required this.riChen, required this.yuePo, required this.riChong, }); factory GanzhiBackend.fromJson(Map json) { return GanzhiBackend( yearGanZhi: _requiredString(json, 'yearGanZhi'), monthGanZhi: _requiredString(json, 'monthGanZhi'), dayGanZhi: _requiredString(json, 'dayGanZhi'), timeGanZhi: _requiredString(json, 'timeGanZhi'), yearKongWang: _requiredString(json, 'yearKongWang'), monthKongWang: _requiredString(json, 'monthKongWang'), dayKongWang: _requiredString(json, 'dayKongWang'), timeKongWang: _requiredString(json, 'timeKongWang'), yueJian: _requiredString(json, 'yueJian'), riChen: _requiredString(json, 'riChen'), yuePo: _requiredString(json, 'yuePo'), riChong: _requiredString(json, 'riChong'), ); } final String yearGanZhi; final String monthGanZhi; final String dayGanZhi; final String timeGanZhi; final String yearKongWang; final String monthKongWang; final String dayKongWang; final String timeKongWang; final String yueJian; final String riChen; final String yuePo; final String riChong; } class YaoBackendLine { const YaoBackendLine({ required this.position, required this.spiritName, required this.relationName, required this.tiganName, required this.elementName, required this.isYang, required this.isChanging, required this.specialMark, }); factory YaoBackendLine.fromJson(Map json) { return YaoBackendLine( position: _requiredInt(json, 'position'), spiritName: _requiredString(json, 'spiritName'), relationName: _requiredString(json, 'relationName'), tiganName: _requiredString(json, 'tiganName'), elementName: _requiredString(json, 'elementName'), isYang: _requiredBool(json, 'isYang'), isChanging: _requiredBool(json, 'isChanging'), specialMark: _requiredStringAllowEmpty(json, 'specialMark'), ); } final int position; final String spiritName; final String relationName; final String tiganName; final String elementName; final bool isYang; final bool isChanging; final String specialMark; YaoLineData toViewModel() { final type = switch ((isYang, isChanging)) { (true, false) => YaoType.youngYang, (false, false) => YaoType.youngYin, (true, true) => YaoType.oldYang, (false, true) => YaoType.oldYin, }; return YaoLineData( index: position - 1, spirit: spiritName, relation: relationName, branch: tiganName, element: elementName, type: type, mark: specialMark, ); } } String _requiredStringAllowEmpty(Map json, String key) { if (!json.containsKey(key)) { throw FormatException('Invalid or missing field: $key'); } final value = json[key]; if (value is! String) { throw FormatException('Invalid or missing field: $key'); } return value; } String _requiredString(Map json, String key) { final value = json[key]; if (value is! String || value.isEmpty) { throw FormatException('Invalid or missing field: $key'); } return value; } int _requiredInt(Map json, String key) { final value = json[key]; if (value is! num) { throw FormatException('Invalid or missing field: $key'); } return value.toInt(); } bool _requiredBool(Map json, String key) { final value = json[key]; if (value is! bool) { throw FormatException('Invalid or missing field: $key'); } return value; } Map _requiredMap(Map json, String key) { final value = json[key]; if (value is! Map) { throw FormatException('Invalid or missing field: $key'); } return value; }