import { createClient } from '@supabase/supabase-js' import type { Problem, LedgerProblem, StatementProblem } from '../types' const url = import.meta.env.VITE_SUPABASE_URL as string | undefined const anonKey = import.meta.env.VITE_SUPABASE_ANON_KEY as string | undefined export const isSupabaseConfigured = Boolean(url && anonKey) export const supabase = isSupabaseConfigured ? createClient(url!, anonKey!) : null /** * 第1問(仕訳)の問題を取得する。 * topic='all' または未指定のとき全論点を取得する。 * ※ isSolvable フィルタは適用しない(利息・消費税など計算問題に対応するため) */ export async function fetchProblems(topic?: string, limit = 20): Promise { if (!supabase) throw new Error('Supabase が設定されていません') let query = supabase .from('jq_problems') .select('id, section, topic, difficulty, event_text, documents, symbols, debit, credit, choices, hint, explanation') .eq('section', 'journal') .limit(limit) if (topic && topic !== 'all') { query = query.eq('topic', topic) } const { data, error } = await query if (error) throw new Error(`問題の取得に失敗しました: ${error.message}`) return (data ?? []) as Problem[] } /** バンクが足りないときに Edge Function で補充生成する */ export async function generateProblems(topic: string, count = 5): Promise { if (!supabase) throw new Error('Supabase が設定されていません') const { data, error } = await supabase.functions.invoke('jq-generate-problems', { body: { topic, count }, }) if (error) throw new Error(`問題の生成に失敗しました: ${error.message}`) return ((data?.problems ?? []) as Problem[]) } /** 第2問(勘定記入)問題を取得する */ export async function fetchLedgerProblems(): Promise { if (!supabase) throw new Error('Supabase が設定されていません') const { data, error } = await supabase .from('jq_problems') .select('id, section, topic, event_text, documents, problem_data, hint, explanation') .eq('section', 'ledger') if (error) throw new Error(`勘定記入問題の取得に失敗しました: ${error.message}`) return (data ?? []).map((row) => { const d = row.problem_data ?? {} return { id: row.id, section: 'ledger' as const, topic: row.topic, account_name: d.account_name ?? '', event_text: row.event_text, documents: row.documents ?? undefined, word_bank: d.word_bank ?? [], rows: d.rows ?? [], hint: row.hint, explanation: row.explanation, } satisfies LedgerProblem }) } /** 第3問(財務諸表)問題を取得する */ export async function fetchStatementProblems(): Promise { if (!supabase) throw new Error('Supabase が設定されていません') const { data, error } = await supabase .from('jq_problems') .select('id, section, topic, event_text, documents, problem_data, hint, explanation') .eq('section', 'statement') if (error) throw new Error(`財務諸表問題の取得に失敗しました: ${error.message}`) return (data ?? []).map((row) => { const d = row.problem_data ?? {} return { id: row.id, section: 'statement' as const, topic: row.topic, event_text: row.event_text, documents: row.documents ?? undefined, bs_rows: d.bs_rows ?? [], hint: row.hint, explanation: row.explanation, } satisfies StatementProblem }) } /** プレイ結果の記録(認証済みユーザーのみ呼ぶ) */ export async function recordAttempt(problemId: string, isCorrect: boolean): Promise { if (!supabase) return const { error } = await supabase .from('jq_attempts') .insert({ problem_id: problemId, is_correct: isCorrect }) if (error) console.warn('recordAttempt failed', error) }