import { DefaultService } from '@/lib/api';
import { AppNodeType } from '../types/node';
import { Edge } from '@xyflow/react';
import { canvasCache } from '@/lib/utils/canvasCache';
import { CanvasMetadata } from '@/types';

const DB_NAME = 'canvas_db';
const DB_VERSION = 1;
const STORE_NAME = 'app_data';

// Keep IndexedDB as fallback or cache
const getDB = (): Promise<IDBDatabase> => {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, DB_VERSION);

    request.onupgradeneeded = (event: any) => {
      const db = event.target.result;
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME);
      }
    };

    request.onsuccess = (event: any) => {
      resolve(event.target.result);
    };

    request.onerror = (event: any) => {
      reject(event.target.error);
    };
  });
};

const saveToLocal = async (key: string, data: any) => {
    try {
        const db = await getDB();
        return new Promise<void>((resolve, reject) => {
          const tx = db.transaction(STORE_NAME, 'readwrite');
          const store = tx.objectStore(STORE_NAME);
          const request = store.put(data, key);

          tx.oncomplete = () => resolve();
          tx.onerror = () => reject(tx.error);
        });
    } catch (e) {
        console.warn('Local storage save failed', e);
    }
};

const loadFromLocal = async <T>(key: string): Promise<T | undefined> => {
    try {
        const db = await getDB();
        return new Promise<T | undefined>((resolve, reject) => {
          const tx = db.transaction(STORE_NAME, 'readonly');
          const store = tx.objectStore(STORE_NAME);
          const request = store.get(key);

          request.onsuccess = () => {
            resolve(request.result);
          };
          request.onerror = () => {
            reject(request.error);
          };
        });
    } catch (e) {
        console.warn('Local storage load failed', e);
        return undefined;
    }
};

export const saveToStorage = async (key: string, data: any) => {
    return saveToLocal(key, data);
};

export const loadFromStorage = async <T>(key: string): Promise<T | undefined> => {
    return loadFromLocal<T>(key);
};

export const saveCanvasStateToStorage = async (
    key: string, 
    data: { nodes: AppNodeType[], edges: Edge[], history?: any[] }, 
    projectId?: string,
    metadata?: CanvasMetadata
) => {
    // Strategy: Optimistic Local Save + Async Server Save + Cache Update
    
    // 1. Save to Local First (Optimistic)
    // We await this to ensure local persistence works.
    // Note: saveToLocal handles its own errors internally and logs warnings.
    await saveToLocal(key, data);

    // 2. Update Cache
    // If metadata is provided, update the cache with the latest data
    if (metadata) {
        try {
            canvasCache.set(key, {
                metadata,
                content: {
                    nodes: data.nodes,
                    edges: data.edges
                }
            });
        } catch (e) {
            console.warn('[Storage] Failed to update cache:', e);
        }
    }

    // 3. Save to Server
    try {
        await DefaultService.saveCanvasState({
            id: key,
            projectId: projectId,
            nodes: data.nodes as any[],
            connections: data.edges.map(e => ({
                id: e.id,
                source: e.source,
                target: e.target,
                sourceHandle: e.sourceHandle,
                targetHandle: e.targetHandle,
                type: e.type,
                data: e.data
            })),
            history: data.history || [],
            updatedAt: Date.now()
        });
    } catch (e) {
        // Propagate error to let the caller (useMutation) handle it (e.g., show UI feedback)
        console.error('[Storage] Server sync failed:', e);
        throw e;
    }
};

export const loadCanvasStateFromStorage = async <T>(
    key: string, 
    projectId?: string,
    metadata?: CanvasMetadata
): Promise<T | undefined> => {
    // Strategy: Cache First → Server → Local Fallback
    
    // 1. Check Cache First
    const cached = canvasCache.get(key);
    if (cached) {
        console.log('[Storage] Cache hit for canvas:', key);
        return {
            nodes: cached.content.nodes,
            edges: cached.content.edges,
            history: []
        } as T;
    }
    
    console.log('[Storage] Cache miss for canvas:', key);

    // 2. Try Load from Server
    try {
        const response = await DefaultService.getCanvasState(key, projectId);
        if (response && response.data) {
            const serverData = response.data as any;
            // Convert server connections back to edges
            const edges = (serverData.connections || []).map((c: any) => ({
                id: c.id,
                source: c.source,
                target: c.target,
                sourceHandle: c.sourceHandle,
                targetHandle: c.targetHandle,
                type: c.type || 'gradient',
                data: c.data
            }));

            const canvasData = {
                nodes: serverData.nodes || [],
                edges: edges,
                history: serverData.history || []
            } as T;

            // 3. Update Cache with Server Data
            if (metadata) {
                try {
                    canvasCache.set(key, {
                        metadata,
                        content: {
                            nodes: serverData.nodes || [],
                            edges: edges
                        }
                    });
                    console.log('[Storage] Canvas cached after server load:', key);
                } catch (e) {
                    console.warn('[Storage] Failed to cache canvas:', e);
                }
            }

            return canvasData;
        }
    } catch (e) {
        console.warn('[Storage] Failed to load canvas state from server, falling back to local', e);
    }

    // 4. Fallback to Local
    const localData = await loadFromLocal<T>(key);
    
    // 5. Cache Local Data if Available
    if (localData && metadata) {
        try {
            const typedData = localData as any;
            canvasCache.set(key, {
                metadata,
                content: {
                    nodes: typedData.nodes || [],
                    edges: typedData.edges || []
                }
            });
            console.log('[Storage] Canvas cached after local load:', key);
        } catch (e) {
            console.warn('[Storage] Failed to cache local canvas:', e);
        }
    }
    
    return localData;
};