'use client'; import { useState, useEffect } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import type { SajuData } from '@/lib/saju-calculator'; import type { DaeunPillar } from '@/lib/daeun-calculator'; import { createBrowserClient } from '@supabase/ssr' import { useRouter } from 'next/navigation'; import { AccordionItem, parseSections, SECTION_ICONS } from './AccordionItem'; import TokenPurchaseModal from './TokenPurchaseModal'; import { ensureProfile } from '@/lib/ensure-profile'; interface AiInterpretationSectionProps { sajuData: SajuData; currentDaeun: DaeunPillar | null; daeunList?: DaeunPillar[]; initialInterpretation?: string | null; } export default function AiInterpretationSection({ sajuData, currentDaeun, daeunList, initialInterpretation }: AiInterpretationSectionProps) { const [interpretation, setInterpretation] = useState(initialInterpretation || null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [isUnlocked, setIsUnlocked] = useState(!!initialInterpretation); const [user, setUser] = useState(null); const [credits, setCredits] = useState(0); const [checkingRecord, setCheckingRecord] = useState(false); const [showTokenModal, setShowTokenModal] = useState(false); const router = useRouter(); const supabase = createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ); // Check auth status on mount and listen for changes useEffect(() => { const getUser = async () => { const { data: { user } } = await supabase.auth.getUser(); setUser(user); if (user) { const currentCredits = await ensureProfile(supabase, user); setCredits(currentCredits); } }; getUser(); const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { setUser(session?.user ?? null); }); return () => { subscription.unsubscribe(); }; }, []); // Check for existing saju_records when user is available useEffect(() => { if (!user || isUnlocked || initialInterpretation) return; const checkExistingRecord = async () => { setCheckingRecord(true); try { const { data: records } = await supabase .from('saju_records') .select('interpretation, saju_data') .eq('user_id', user.id); if (records && records.length > 0) { const match = records.find((r: any) => { const rd = r.saju_data; return ( rd.birthDate?.year === sajuData.birthDate.year && rd.birthDate?.month === sajuData.birthDate.month && rd.birthDate?.day === sajuData.birthDate.day && rd.gender === sajuData.gender ); }); if (match) { setInterpretation(match.interpretation); setIsUnlocked(true); } } } catch (err) { console.error('Failed to check existing records:', err); } finally { setCheckingRecord(false); } }; checkExistingRecord(); }, [user]); const handleTokenUse = async () => { if (!user) { if (confirm('로그인이 필요합니다. 로그인 페이지로 이동하시겠습니까?')) { router.push('/login'); } return; } if (credits < 1) { setShowTokenModal(true); return; } // 토큰 차감 const { error: updateError } = await supabase .from('profiles') .update({ credits: credits - 1 }) .eq('id', user.id); if (updateError) { alert('토큰 차감에 실패했습니다. 다시 시도해주세요.'); return; } setCredits(credits - 1); setLoading(true); setIsUnlocked(true); await fetchInterpretationAndSave(); }; const handlePurchaseComplete = async () => { setShowTokenModal(false); if (user) { const { data: profile } = await supabase .from('profiles') .select('credits') .eq('id', user.id) .single(); if (profile) setCredits(profile.credits || 0); } }; const fetchInterpretationAndSave = async () => { try { setLoading(true); setError(null); const response = await fetch('/api/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ saju: sajuData, daeun: currentDaeun, daeunList: daeunList || [], gender: sajuData.gender }), }); if (!response.ok) throw new Error('Analysis failed'); const data = await response.json(); const newInterp = data.interpretation; setInterpretation(newInterp); if (user) { // 프로필 존재 보장 (FK 위반 방지) await ensureProfile(supabase, user); // Save Record const { error: insertError } = await supabase.from('saju_records').insert({ user_id: user.id, saju_data: sajuData, interpretation: newInterp, }); if (insertError) { console.error('saju_records 저장 실패:', insertError.message, insertError.details); } } } catch (err) { console.error(err); setError('분석을 불러오는 중 오류가 발생했습니다.'); } finally { setLoading(false); } }; // If initialInterpretation is provided, we simulate "already unlocked" useEffect(() => { if (initialInterpretation) { setIsUnlocked(true); setInterpretation(initialInterpretation); } }, [initialInterpretation]); // Loading View if (loading || checkingRecord) { return (

{checkingRecord ? '기존 기록을 확인하고 있습니다...' : '사주를 분석하고 있습니다...'}

{checkingRecord ? '잠시만 기다려주세요.' : <>천간과 지지의 조화, 대운의 흐름을 깊이 있게 해석 중입니다.
잠시만 기다려주세요. }

); } // Locked View (Preview) if (!isUnlocked) { return ( <>
🔐

전문가 심층 분석 잠금해제

AI 명리학자의 소름 돋는 인생 해석을 확인하세요.

토큰 10개 사용 | 보유: {credits}개

* 결제 후 평생 소장 가능합니다.
* 같은 생년월일 재접근 시 토큰이 차감되지 않아요.

{/* Blurred Content Placeholder */}

당신을 위한 맞춤 심층 분석

setShowTokenModal(false)} onPurchaseComplete={handlePurchaseComplete} user={user} supabase={supabase} /> ); } // Unlocked View if (error) { return (

⚠️ {error}

); } const sections = interpretation ? parseSections(interpretation) : []; return (

✨ 당신을 위한 맞춤 심층 분석

AI 사주 상담사가 당신만을 위해 정성껏 풀어봤어요.

{user && 잠금해제 완료 / 평생 소장}
{sections.length > 0 ? (
{sections.map((section, idx) => ( ))}
) : (
{interpretation || ''}
)}
💡

참고하세요

이 분석은 전통 명리학 알고리즘과 AI를 결합하여 만들어졌어요. 인생의 큰 흐름을 이해하는 데 도움이 되길 바라며, 모든 선택의 주인공은 언제나 당신이라는 걸 잊지 마세요.

); }