﻿import { db } from './db';
import { syncEngine } from './sync-engine';

export type CatalogType = 'tipo_vehiculo' | 'cosa_trae' | 'tipo_dano';

type AnyCatalogItem = {
  id: number;
  nombre: string;
  activo?: boolean;
  descripcion?: string;
  plantillaDanos?: string;
  icono?: string;
  color?: string;
};
function getOfflineSeed(type: CatalogType): AnyCatalogItem[] {
  if (type === 'tipo_vehiculo') {
    return [
      { id: -101, nombre: 'Carro', descripcion: '', plantillaDanos: 'carro', activo: true },
      { id: -102, nombre: 'Moto', descripcion: '', plantillaDanos: 'moto', activo: true },
      { id: -103, nombre: 'Camion', descripcion: '', plantillaDanos: 'camion', activo: true },
    ];
  }

  if (type === 'tipo_dano') {
    return [
      { id: -201, nombre: 'Golpe', icono: 'golpe', color: '#ef4444', activo: true },
      { id: -202, nombre: 'Rayon', icono: 'rayon', color: '#f59e0b', activo: true },
      { id: -203, nombre: 'Quebrado', icono: 'quebrado', color: '#dc2626', activo: true },
    ];
  }

  return [
    { id: -301, nombre: 'Llanta de repuesto', descripcion: '', activo: true },
    { id: -302, nombre: 'Herramientas', descripcion: '', activo: true },
    { id: -303, nombre: 'Extintor', descripcion: '', activo: true },
  ];
}

function isNetworkLikeError(error: any): boolean {
  if (typeof navigator !== 'undefined' && !navigator.onLine) {
    return true;
  }

  const status = Number(error?.status);
  if (Number.isFinite(status) && status >= 500) {
    return true;
  }

  const msg = String(error?.message || '');
  return /Failed to fetch|NetworkError|ERR_EMPTY_RESPONSE|ECONNREFUSED|ECONNRESET|Internal server error/i.test(msg);
}

function toCacheRecord(type: CatalogType, item: AnyCatalogItem) {
  const base = {
    id: Number(item.id),
    tipo: type,
    nombre: item.nombre,
    descripcion: item.descripcion || undefined,
    cachedAt: Date.now(),
  } as any;

  if (type === 'tipo_vehiculo') {
    base.extra = {
      plantillaDanos: item.plantillaDanos || 'carro',
      activo: item.activo !== false,
    };
  } else if (type === 'tipo_dano') {
    base.extra = {
      icono: item.icono || '',
      color: item.color || '#ef4444',
      activo: item.activo !== false,
    };
  } else {
    base.extra = {
      activo: item.activo !== false,
    };
  }

  return base;
}

function fromCacheRecord(type: CatalogType, row: any): AnyCatalogItem {
  if (type === 'tipo_vehiculo') {
    return {
      id: row.id,
      nombre: row.nombre,
      descripcion: row.descripcion,
      plantillaDanos: row.extra?.plantillaDanos || 'carro',
      activo: row.extra?.activo !== false,
    };
  }

  if (type === 'tipo_dano') {
    return {
      id: row.id,
      nombre: row.nombre,
      icono: row.extra?.icono || '',
      color: row.extra?.color || '#ef4444',
      activo: row.extra?.activo !== false,
    };
  }

  return {
    id: row.id,
    nombre: row.nombre,
    descripcion: row.descripcion,
    activo: row.extra?.activo !== false,
  };
}

async function getCached(type: CatalogType): Promise<AnyCatalogItem[]> {
  const rows = await db.catalogos.where('tipo').equals(type).toArray();
  return rows
    .map((r) => fromCacheRecord(type, r))
    .filter((r) => r.activo !== false)
    .sort((a, b) => (a.nombre || '').localeCompare(b.nombre || ''));
}

export async function cacheCatalogList(type: CatalogType, items: AnyCatalogItem[]): Promise<void> {
  await db.transaction('rw', db.catalogos, async () => {
    const pendingLocal = (await db.catalogos.where('tipo').equals(type).toArray())
      .filter((row: any) => Number(row.id) < 0);

    await db.catalogos.where('tipo').equals(type).delete();

    const remoteRows = items.map((item) => toCacheRecord(type, item));
    const mergedRows = [...remoteRows, ...pendingLocal];

    if (mergedRows.length > 0) {
      await db.catalogos.bulkPut(mergedRows);
    }
  });
}

function eventTypeFor(type: CatalogType, action: 'CREATE' | 'UPDATE' | 'INACTIVATE') {
  if (type === 'tipo_vehiculo') return `${action}_TIPO_VEHICULO`;
  if (type === 'tipo_dano') return `${action}_TIPO_DANO`;
  return `${action}_COSA_TRAE`;
}

export async function fetchCatalogWithCache(
  type: CatalogType,
  fetchRemote: () => Promise<AnyCatalogItem[]>
): Promise<AnyCatalogItem[]> {
  try {
    const remote = await fetchRemote();
    await cacheCatalogList(type, remote);
    return remote.filter((x) => x.activo !== false);
  } catch (error) {
    console.warn(`[catalogos] Fallback cache for ${type}`, error);
    return getCached(type);
  }
}

export async function createCatalogWithOffline(
  type: CatalogType,
  data: Record<string, any>,
  createRemote: (data: any) => Promise<any>
): Promise<any> {
  try {
    const created = await createRemote(data);
    await db.catalogos.put(toCacheRecord(type, created));
    return created;
  } catch (error: any) {
    if (!isNetworkLikeError(error)) {
      throw error;
    }

    const tempId = -Date.now();
    const localItem = { id: tempId, ...data, activo: true };

    await db.catalogos.put(toCacheRecord(type, localItem as AnyCatalogItem));

    await syncEngine.queueEvent({
      eventId: crypto.randomUUID(),
      eventType: eventTypeFor(type, 'CREATE'),
      entityType: `catalog_${type}`,
      localId: `catalog:${type}:${tempId}`,
      payload: data,
    });

    return { ...localItem, offlineQueued: true };
  }
}

export async function updateCatalogWithOffline(
  type: CatalogType,
  id: number,
  data: Record<string, any>,
  updateRemote: (id: number, data: any) => Promise<any>
): Promise<any> {
  try {
    const updated = await updateRemote(id, data);
    await db.catalogos.put(toCacheRecord(type, updated));
    return updated;
  } catch (error: any) {
    if (!isNetworkLikeError(error)) {
      throw error;
    }

    const existing = await db.catalogos.get([type, id]);
    const merged = {
      id,
      nombre: data.nombre ?? existing?.nombre,
      descripcion: data.descripcion ?? existing?.descripcion,
      plantillaDanos: data.plantillaDanos ?? existing?.extra?.plantillaDanos,
      icono: data.icono ?? existing?.extra?.icono,
      color: data.color ?? existing?.extra?.color,
      activo: existing?.extra?.activo !== false,
    };

    await db.catalogos.put(toCacheRecord(type, merged as AnyCatalogItem));

    const payload: any = { ...data };
    if (id > 0) {
      payload.id = id;
    }

    await syncEngine.queueEvent({
      eventId: crypto.randomUUID(),
      eventType: eventTypeFor(type, 'UPDATE'),
      entityType: `catalog_${type}`,
      localId: `catalog:${type}:${id}`,
      payload,
    });

    return { ...merged, offlineQueued: true };
  }
}

export async function inactivateCatalogWithOffline(
  type: CatalogType,
  id: number,
  inactivateRemote: (id: number) => Promise<any>
): Promise<any> {
  try {
    const result = await inactivateRemote(id);
    await db.catalogos.where('[tipo+id]').equals([type, id]).delete();
    return result;
  } catch (error: any) {
    if (!isNetworkLikeError(error)) {
      throw error;
    }

    if (id < 0) {
      await db.catalogos.where('[tipo+id]').equals([type, id]).delete();
      return { offlineQueued: true, localOnly: true };
    }

    await db.catalogos.where('[tipo+id]').equals([type, id]).modify((row: any) => {
      row.extra = { ...(row.extra || {}), activo: false };
    });

    await syncEngine.queueEvent({
      eventId: crypto.randomUUID(),
      eventType: eventTypeFor(type, 'INACTIVATE'),
      entityType: `catalog_${type}`,
      localId: `catalog:${type}:${id}`,
      payload: { id },
    });

    return { offlineQueued: true };
  }
}


export async function getPendingCatalogIds(type: CatalogType): Promise<number[]> {
  const prefix = `catalog:${type}:`;
  const events = await db.syncEvents
    .where('status')
    .anyOf(['pending', 'failed'])
    .toArray();

  const ids = new Set<number>();

  for (const event of events) {
    if (!String(event.localId || '').startsWith(prefix)) {
      continue;
    }

    const parts = String(event.localId).split(':');
    const parsedId = Number(parts[2]);
    if (Number.isFinite(parsedId)) {
      ids.add(parsedId);
    }
  }

  return Array.from(ids);
}


