import { supabaseRest } from "@/lib/supabaseRest"; export interface DistillationRequest { id: string; sourceDataset: string; studentModel: string; submitterName?: string; additionalNotes: string; upvotes: number; votedIps: string[]; ownerId: string; createdAt: string; status: "pending" | "in_progress" | "completed"; } export interface DatasetRequest { id: string; sourceModel: string; submitterName?: string; datasetSize: string; reasoningDepth: string; topics: string[]; additionalNotes: string; upvotes: number; votedIps: string[]; ownerId: string; createdAt: string; status: "pending" | "in_progress" | "completed"; } export type RequestType = "distillation" | "dataset"; export interface DiscussionComment { id: string; body: string; author: string; role: "admin" | "user"; ownerId: string; createdAt: string; editedAt?: string; } export interface DiscussionThread { key: string; requestType: RequestType; requestId: string; comments: DiscussionComment[]; } interface Store { distillationRequests: DistillationRequest[]; datasetRequests: DatasetRequest[]; threads: Record; } function normalizeStatus(value: unknown): "pending" | "in_progress" | "completed" { return value === "in_progress" || value === "completed" ? value : "pending"; } function mapDistillationRow(row: any): DistillationRequest { const submitterNameRaw = typeof row?.submitter_name === "string" ? row.submitter_name.trim() : ""; return { id: String(row?.id ?? ""), sourceDataset: String(row?.source_dataset ?? ""), studentModel: String(row?.student_model ?? ""), submitterName: submitterNameRaw ? submitterNameRaw : undefined, additionalNotes: String(row?.additional_notes ?? ""), upvotes: typeof row?.upvotes === "number" ? row.upvotes : 0, votedIps: Array.isArray(row?.voted_ips) ? row.voted_ips.map(String) : [], ownerId: String(row?.owner_id ?? ""), createdAt: String(row?.created_at ?? new Date().toISOString()), status: normalizeStatus(row?.status), }; } function mapDatasetRow(row: any): DatasetRequest { const submitterNameRaw = typeof row?.submitter_name === "string" ? row.submitter_name.trim() : ""; return { id: String(row?.id ?? ""), sourceModel: String(row?.source_model ?? ""), submitterName: submitterNameRaw ? submitterNameRaw : undefined, datasetSize: String(row?.dataset_size ?? "250x"), reasoningDepth: String(row?.reasoning_depth ?? "high"), topics: Array.isArray(row?.topics) ? row.topics.map(String) : [], additionalNotes: String(row?.additional_notes ?? ""), upvotes: typeof row?.upvotes === "number" ? row.upvotes : 0, votedIps: Array.isArray(row?.voted_ips) ? row.voted_ips.map(String) : [], ownerId: String(row?.owner_id ?? ""), createdAt: String(row?.created_at ?? new Date().toISOString()), status: normalizeStatus(row?.status), }; } function mapCommentRow(row: any): DiscussionComment { return { id: String(row?.id ?? ""), body: String(row?.body ?? ""), author: String(row?.author ?? (row?.role === "user" ? "Anonymous" : "TeichAI")), role: row?.role === "user" ? "user" : "admin", ownerId: String(row?.owner_id ?? ""), createdAt: String(row?.created_at ?? new Date().toISOString()), editedAt: row?.edited_at ? String(row.edited_at) : undefined, }; } export async function getDistillationRequests(): Promise { const { data, error } = await supabaseRest("/rest/v1/distillation_requests", { query: { select: "*", order: "upvotes.desc,created_at.desc", }, }); if (error) { throw new Error(error.message); } return Array.isArray(data) ? data.map(mapDistillationRow) : []; } export async function getDatasetRequests(): Promise { const { data, error } = await supabaseRest("/rest/v1/dataset_requests", { query: { select: "*", order: "upvotes.desc,created_at.desc", }, }); if (error) { throw new Error(error.message); } return Array.isArray(data) ? data.map(mapDatasetRow) : []; } export async function getRequest(type: RequestType, id: string): Promise { const table = type === "distillation" ? "distillation_requests" : "dataset_requests"; const { data, error } = await supabaseRest(`/rest/v1/${table}`, { query: { select: "*", id: `eq.${id}`, }, }); if (error) { throw new Error(error.message); } const row = Array.isArray(data) ? data[0] : null; if (!row) return null; return type === "distillation" ? mapDistillationRow(row) : mapDatasetRow(row); } export async function updateDistillationRequest( id: string, updates: Partial> ): Promise { const body: Record = {}; if (typeof updates.sourceDataset === "string") body.source_dataset = updates.sourceDataset; if (typeof updates.studentModel === "string") body.student_model = updates.studentModel; if (typeof updates.additionalNotes === "string") body.additional_notes = updates.additionalNotes; const { data, error } = await supabaseRest("/rest/v1/distillation_requests", { method: "PATCH", body: JSON.stringify(body), query: { select: "*", id: `eq.${id}` }, preferReturn: "representation", requireServiceRole: true, }); if (error) { throw new Error(error.message); } const row = Array.isArray(data) ? data[0] : null; return row ? mapDistillationRow(row) : null; } export async function updateDatasetRequest( id: string, updates: Partial> ): Promise { const body: Record = {}; if (typeof updates.sourceModel === "string") body.source_model = updates.sourceModel; if (typeof updates.datasetSize === "string") body.dataset_size = updates.datasetSize; if (typeof updates.reasoningDepth === "string") body.reasoning_depth = updates.reasoningDepth; if (Array.isArray(updates.topics)) body.topics = updates.topics.map(String); if (typeof updates.additionalNotes === "string") body.additional_notes = updates.additionalNotes; const { data, error } = await supabaseRest("/rest/v1/dataset_requests", { method: "PATCH", body: JSON.stringify(body), query: { select: "*", id: `eq.${id}` }, preferReturn: "representation", requireServiceRole: true, }); if (error) { throw new Error(error.message); } const row = Array.isArray(data) ? data[0] : null; return row ? mapDatasetRow(row) : null; } export async function updateRequestStatus( type: RequestType, id: string, status: "pending" | "in_progress" | "completed" ): Promise { const table = type === "distillation" ? "distillation_requests" : "dataset_requests"; const { data, error } = await supabaseRest(`/rest/v1/${table}`, { method: "PATCH", body: JSON.stringify({ status }), query: { select: "id", id: `eq.${id}` }, preferReturn: "representation", requireServiceRole: true, }); if (error) { throw new Error(error.message); } return Array.isArray(data) ? Boolean(data[0]?.id) : false; } export async function deleteRequest(type: RequestType, id: string): Promise { await supabaseRest("/rest/v1/request_comments", { method: "DELETE", query: { request_type: `eq.${type}`, request_id: `eq.${id}`, }, requireServiceRole: true, }); const table = type === "distillation" ? "distillation_requests" : "dataset_requests"; const { data, error } = await supabaseRest(`/rest/v1/${table}`, { method: "DELETE", body: null, query: { select: "id", id: `eq.${id}` }, preferReturn: "representation", requireServiceRole: true, }); if (error) { throw new Error(error.message); } return Array.isArray(data) ? Boolean(data[0]?.id) : false; } export async function getThread(type: RequestType, id: string): Promise { const key = `${type}:${id}`; const { data, error } = await supabaseRest("/rest/v1/request_comments", { query: { select: "*", request_type: `eq.${type}`, request_id: `eq.${id}`, order: "created_at.asc", }, }); if (error) { throw new Error(error.message); } return { key, requestType: type, requestId: id, comments: Array.isArray(data) ? data.map(mapCommentRow) : [], }; } export async function addAdminComment(type: RequestType, id: string, body: string, ownerId: string): Promise { const { data, error } = await supabaseRest("/rest/v1/request_comments", { method: "POST", body: JSON.stringify({ request_type: type, request_id: id, body, author: "TeichAI", role: "admin", owner_id: ownerId, }), query: { select: "*" }, preferReturn: "representation", requireServiceRole: true, }); if (error) { throw new Error(error.message); } const row = Array.isArray(data) ? data[0] : null; if (!row) { throw new Error("Failed to create comment"); } return mapCommentRow(row); } export async function addUserComment( type: RequestType, id: string, body: string, author: string | undefined, ownerId: string ): Promise { const { data, error } = await supabaseRest("/rest/v1/request_comments", { method: "POST", body: JSON.stringify({ request_type: type, request_id: id, body, author: author?.trim() ? author.trim() : "Anonymous", role: "user", owner_id: ownerId, }), query: { select: "*" }, preferReturn: "representation", acceptObject: true, }); if (error) { throw new Error(error.message); } if (!data) { throw new Error("Failed to create comment"); } return mapCommentRow(data); } export async function updateComment( type: RequestType, requestId: string, commentId: string, body: string ): Promise { const { data, error } = await supabaseRest("/rest/v1/request_comments", { method: "PATCH", body: JSON.stringify({ body, edited_at: new Date().toISOString() }), query: { select: "*", id: `eq.${commentId}`, request_type: `eq.${type}`, request_id: `eq.${requestId}`, }, preferReturn: "representation", requireServiceRole: true, }); if (error) { throw new Error(error.message); } const row = Array.isArray(data) ? data[0] : null; return row ? mapCommentRow(row) : null; } export async function deleteComment( type: RequestType, requestId: string, commentId: string ): Promise { const { data, error } = await supabaseRest("/rest/v1/request_comments", { method: "DELETE", body: null, query: { select: "id", id: `eq.${commentId}`, request_type: `eq.${type}`, request_id: `eq.${requestId}`, }, preferReturn: "representation", requireServiceRole: true, }); if (error) { throw new Error(error.message); } return Array.isArray(data) ? Boolean(data[0]?.id) : false; } export async function addDistillationRequest( request: Omit ): Promise { const { data, error } = await supabaseRest("/rest/v1/distillation_requests", { method: "POST", body: JSON.stringify({ source_dataset: request.sourceDataset, student_model: request.studentModel, submitter_name: request.submitterName ? request.submitterName : null, additional_notes: request.additionalNotes, owner_id: request.ownerId, }), query: { select: "*" }, preferReturn: "representation", acceptObject: true, }); if (error) { throw new Error(error.message); } if (!data) { throw new Error("Failed to create request"); } return mapDistillationRow(data); } export async function addDatasetRequest( request: Omit ): Promise { const { data, error } = await supabaseRest("/rest/v1/dataset_requests", { method: "POST", body: JSON.stringify({ source_model: request.sourceModel, submitter_name: request.submitterName ? request.submitterName : null, dataset_size: request.datasetSize, reasoning_depth: request.reasoningDepth, topics: request.topics, additional_notes: request.additionalNotes, owner_id: request.ownerId, }), query: { select: "*" }, preferReturn: "representation", acceptObject: true, }); if (error) { throw new Error(error.message); } if (!data) { throw new Error("Failed to create request"); } return mapDatasetRow(data); } export async function upvoteDistillation( id: string, ip: string ): Promise<{ success: boolean; upvotes: number; action?: "upvoted" | "unvoted" }> { const { data, error } = await supabaseRest("/rest/v1/rpc/toggle_upvote_distillation", { method: "POST", body: JSON.stringify({ request_id: id, voter_ip: ip }), }); if (error) { throw new Error(error.message); } const row = Array.isArray(data) ? data[0] : null; if (!row) { return { success: false, upvotes: 0 }; } return { success: Boolean(row.success), upvotes: typeof row.upvotes === "number" ? row.upvotes : 0, action: row.action === "upvoted" || row.action === "unvoted" ? row.action : undefined, }; } export async function upvoteDataset( id: string, ip: string ): Promise<{ success: boolean; upvotes: number; action?: "upvoted" | "unvoted" }> { const { data, error } = await supabaseRest("/rest/v1/rpc/toggle_upvote_dataset", { method: "POST", body: JSON.stringify({ request_id: id, voter_ip: ip }), }); if (error) { throw new Error(error.message); } const row = Array.isArray(data) ? data[0] : null; if (!row) { return { success: false, upvotes: 0 }; } return { success: Boolean(row.success), upvotes: typeof row.upvotes === "number" ? row.upvotes : 0, action: row.action === "upvoted" || row.action === "unvoted" ? row.action : undefined, }; }