"use client"; import { useCallback, useState, type Dispatch, type SetStateAction } from "react"; import { showChatNotice as setComposerNotice } from "@/lib/chat-notice"; import { saveCloudSynastryReport } from "@/lib/home-cloud-sync"; import { buildSynastryQuestion } from "@/lib/home-profile"; import { timestamp, type ChartLibraryRecord, type Profile, type SynastryRelationshipType, type SynastryReportCard, type Theme, } from "@/lib/home-types"; export type SynastryParams = { accountId: string | undefined; profile: Profile; chooseSuggestedQuestion: (question: string, theme: Theme) => void; closeAccountDialog: () => void; }; export type SynastryPanel = { synastryRelationshipType: SynastryRelationshipType; setSynastryRelationshipType: Dispatch>; synastryPendingId: string | null; synastryReportCard: SynastryReportCard | null; setSynastryReportCard: Dispatch>; synastryHistory: SynastryReportCard[]; draftSynastryQuestionFromChart: (record: ChartLibraryRecord, relationshipType: SynastryRelationshipType) => Promise; }; /** * Owns the four synastry states that used to sit in Home(). Home keeps only the * two stable history hooks the chart-library effect calls; everything else the * chart-library dialog needs travels as one `synastryPanel` object. */ export function useSynastry(params: SynastryParams) { const { accountId, profile, chooseSuggestedQuestion, closeAccountDialog } = params; const [synastryRelationshipType, setSynastryRelationshipType] = useState("romance"); const [synastryPendingId, setSynastryPendingId] = useState(null); const [synastryReportCard, setSynastryReportCard] = useState(null); const [synastryHistory, setSynastryHistory] = useState([]); // Stable across renders so Home's chart-library effect can list them in its // dependency array without gaining a re-run. const clearSynastryHistory = useCallback(() => { setSynastryHistory([]); }, []); const applyCloudSynastryHistory = useCallback((cloudHistory: SynastryReportCard[]) => { setSynastryHistory([...cloudHistory].sort((a, b) => b.createdAt - a.createdAt).slice(0, 10)); }, []); async function draftSynastryQuestionFromChart(record: ChartLibraryRecord, relationshipType: SynastryRelationshipType) { if (record.role !== "other") return; if (synastryPendingId) return; const baseQuestion = buildSynastryQuestion(profile, record.profile, relationshipType); setSynastryPendingId(record.id); setComposerNotice("正在计算基础合盘证据,请稍候。"); try { const response = await fetch("/api/synastry", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ partnerChartProfileId: record.id, relationshipType }), }); const payload = await response.json().catch(() => null) as { status?: string; claimStatus?: string; blockedLayers?: string[]; evidenceLayers?: string[]; synastry?: { total_score?: number; max_score?: number; assessment?: string }; relationshipReport?: { headline?: string; scoreBand?: string; strengths?: string[]; risks?: string[]; nextEvidence?: string[] } } | null; if (response.ok && payload?.status === "ok") { const score = payload.synastry?.total_score; const max = payload.synastry?.max_score; const assessment = payload.synastry?.assessment; const layers = (payload.evidenceLayers || []).join(" / ") || "Ashtakoot / Moon / D9"; const evidenceSummary = relationshipType === "business" ? `已完成基础商业合作证据筛查:${layers};声明状态:${payload.claimStatus || "partial"};未用层:${(payload.blockedLayers || []).join(" / ") || "A10 / 双方 Dasha-Narayana / 功能吉凶"}。请勿将其表述为合作保证或精确时点。` : `已计算基础合盘证据:${layers};Ashtakoot ${score ?? "?"}/${max ?? "?"},初步评级:${assessment || "待解释"}。请基于这个证据包继续分析。`; const reportCard: SynastryReportCard = { id: `${record.id}-${timestamp()}`, partnerName: record.profile.name || "对方", partnerChartId: record.id, score, maxScore: max, assessment, headline: payload.relationshipReport?.headline, scoreBand: payload.relationshipReport?.scoreBand, strengths: payload.relationshipReport?.strengths, risks: payload.relationshipReport?.risks, nextEvidence: payload.relationshipReport?.nextEvidence, createdAt: timestamp(), }; let savedReportCard = reportCard; let historyPersisted = !accountId; if (accountId) { try { savedReportCard = await saveCloudSynastryReport(reportCard); historyPersisted = true; } catch { historyPersisted = false; } } setSynastryReportCard(savedReportCard); if (accountId && historyPersisted) { setSynastryHistory((current) => ( [savedReportCard, ...current.filter((item) => item.id !== savedReportCard.id)].slice(0, 10) )); } chooseSuggestedQuestion([ baseQuestion, "", evidenceSummary, payload.relationshipReport?.headline ? `结构化摘要:${payload.relationshipReport.headline}` : "", ].join("\n"), relationshipType === "business" ? "career" : "marriage"); if (accountId && !historyPersisted) { setComposerNotice("未能存入历史"); } } else { chooseSuggestedQuestion(baseQuestion, relationshipType === "business" ? "career" : "marriage"); setComposerNotice(response.status === 404 ? "请先把对方星盘保存到云端,再用于合盘。" : response.status === 429 ? "合盘请求过于频繁,请稍后再试。" : payload?.status === "blocked" ? "合盘计算暂时不可用,已先生成问题草稿。" : "已生成合盘问题草稿。"); } } catch { chooseSuggestedQuestion(baseQuestion, relationshipType === "business" ? "career" : "marriage"); setComposerNotice("合盘计算暂时不可用,已先生成问题草稿。"); } finally { setSynastryPendingId(null); } closeAccountDialog(); } const synastryPanel: SynastryPanel = { synastryRelationshipType, setSynastryRelationshipType, synastryPendingId, synastryReportCard, setSynastryReportCard, synastryHistory, draftSynastryQuestionFromChart, }; return { synastryPanel, clearSynastryHistory, applyCloudSynastryHistory }; }