/**
 * Supabase Storage Adapter
 *
 * Implements CanvasStorageAdapter using Supabase as the backend.
 * Wraps the existing query functions into the adapter interface.
 */

import type { SupabaseClient } from '@supabase/supabase-js';
import type { CanvasStorageAdapter, CanvasNode, CanvasEdge, CanvasChangeEvent } from './adapter';

export class SupabaseStorageAdapter implements CanvasStorageAdapter {
	constructor(private client: SupabaseClient) {}

	/** Get the underlying Supabase client (for advanced use or backward compat) */
	getClient(): SupabaseClient {
		return this.client;
	}

	// --- Node Operations ---

	async fetchNodes(graphId: string): Promise<CanvasNode[]> {
		const { data, error } = await this.client
			.from('nodes')
			.select('*')
			.eq('graph_id', graphId)
			.order('created_at', { ascending: true });

		if (error) throw error;
		return (data as CanvasNode[]) || [];
	}

	async createNode(graphId: string, node: Partial<CanvasNode>): Promise<CanvasNode> {
		const { data, error } = await this.client
			.from('nodes')
			.insert({
				graph_id: graphId,
				label: node.label ?? null,
				node_type: node.node_type ?? null,
				configuration: node.configuration ?? null,
				ui_properties: node.ui_properties ?? null,
				data: node.data ?? null,
			})
			.select()
			.single();

		if (error) throw error;
		return data as CanvasNode;
	}

	async updateNode(nodeId: string, updates: Partial<CanvasNode>): Promise<CanvasNode> {
		const { data, error } = await this.client
			.from('nodes')
			.update({ ...updates, updated_at: new Date().toISOString() })
			.eq('id', nodeId)
			.select()
			.single();

		if (error) throw error;
		return data as CanvasNode;
	}

	async deleteNode(nodeId: string): Promise<void> {
		const { error } = await this.client.from('nodes').delete().eq('id', nodeId);
		if (error) throw error;
	}

	// --- Edge Operations ---

	async fetchEdges(graphId: string): Promise<CanvasEdge[]> {
		const { data, error } = await this.client
			.from('edges')
			.select('*')
			.eq('graph_id', graphId)
			.order('created_at', { ascending: true });

		if (error) throw error;
		return (data as CanvasEdge[]) || [];
	}

	async createEdge(graphId: string, edge: Partial<CanvasEdge>): Promise<CanvasEdge> {
		const { data, error } = await this.client
			.from('edges')
			.insert({
				graph_id: graphId,
				source_node_id: edge.source_node_id ?? '',
				target_node_id: edge.target_node_id ?? '',
				edge_type: edge.edge_type ?? null,
				filter_condition: edge.filter_condition ?? null,
				ui_properties: edge.ui_properties ?? null,
				data: edge.data ?? null,
			})
			.select()
			.single();

		if (error) throw error;
		return data as CanvasEdge;
	}

	async updateEdge(edgeId: string, updates: Partial<CanvasEdge>): Promise<CanvasEdge> {
		const { data, error } = await this.client
			.from('edges')
			.update({ ...updates, updated_at: new Date().toISOString() })
			.eq('id', edgeId)
			.select()
			.single();

		if (error) throw error;
		return data as CanvasEdge;
	}

	async deleteEdge(edgeId: string): Promise<void> {
		const { error } = await this.client.from('edges').delete().eq('id', edgeId);
		if (error) throw error;
	}

	// --- Realtime (optional) ---

	subscribe(graphId: string, onChange: (event: CanvasChangeEvent) => void): () => void {
		const channel = this.client
			.channel(`canvas-${graphId}`)
			.on(
				'postgres_changes',
				{ event: '*', schema: 'public', table: 'nodes', filter: `graph_id=eq.${graphId}` },
				(payload) => {
					onChange({
						type: payload.eventType as 'INSERT' | 'UPDATE' | 'DELETE',
						table: 'nodes',
						new: payload.new as CanvasNode | undefined,
						old: payload.old as Partial<CanvasNode> | undefined,
					});
				},
			)
			.on(
				'postgres_changes',
				{ event: '*', schema: 'public', table: 'edges', filter: `graph_id=eq.${graphId}` },
				(payload) => {
					onChange({
						type: payload.eventType as 'INSERT' | 'UPDATE' | 'DELETE',
						table: 'edges',
						new: payload.new as CanvasEdge | undefined,
						old: payload.old as Partial<CanvasEdge> | undefined,
					});
				},
			)
			.subscribe();

		return () => {
			channel.unsubscribe();
		};
	}
}
