const DB_NAME = 'hipograf-model-cache';
const STORE_NAME = 'models';
const DB_VERSION = 1;

interface CachedModel {
  key: string;
  data: ArrayBuffer;
  timestamp: number;
}

let dbInstance: IDBDatabase | null = null;

async function getDatabase(): Promise<IDBDatabase> {
  if (dbInstance) {
    return dbInstance;
  }

  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, DB_VERSION);

    request.onerror = () => reject(request.error);
    request.onsuccess = () => {
      dbInstance = request.result;
      resolve(dbInstance);
    };

    request.onupgradeneeded = (event) => {
      const db = (event.target as IDBOpenDBRequest).result;
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME, { keyPath: 'key' });
      }
    };
  });
}

export async function getCachedModel(modelKey: string): Promise<ArrayBuffer | null> {
  try {
    const db = await getDatabase();
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readonly');
      const store = transaction.objectStore(STORE_NAME);
      const request = store.get(modelKey);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        const result = request.result as CachedModel | undefined;
        resolve(result?.data || null);
      };
    });
  } catch (error) {
    console.warn('Failed to retrieve model from cache:', error);
    return null;
  }
}

export async function cacheModel(modelKey: string, data: ArrayBuffer): Promise<void> {
  try {
    const db = await getDatabase();
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readwrite');
      const store = transaction.objectStore(STORE_NAME);
      const request = store.put({
        key: modelKey,
        data,
        timestamp: Date.now(),
      } as CachedModel);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => resolve();
    });
  } catch (error) {
    console.warn('Failed to cache model:', error);
  }
}

export async function fetchModelWithCache(modelPath: string): Promise<ArrayBuffer> {
  const cacheKey = `model-${modelPath}`;

  // Try to get from cache first
  const cached = await getCachedModel(cacheKey);
  if (cached) {
    return cached;
  }

  // Fetch from server
  const response = await fetch(modelPath);
  if (!response.ok) {
    throw new Error(`Failed to fetch model: ${response.statusText}`);
  }

  const arrayBuffer = await response.arrayBuffer();

  // Cache it for future use
  await cacheModel(cacheKey, arrayBuffer);

  return arrayBuffer;
}

export function clearModelCache(): Promise<void> {
  return new Promise(async (resolve, reject) => {
    try {
      const db = await getDatabase();
      const transaction = db.transaction([STORE_NAME], 'readwrite');
      const store = transaction.objectStore(STORE_NAME);
      const request = store.clear();

      request.onerror = () => reject(request.error);
      request.onsuccess = () => resolve();
    } catch (error) {
      reject(error);
    }
  });
}
