/**
 * OT Service - handles OT creation and offline queuing
 * Architecture: Layered, offline-first, event-driven
 */

import { v4 as uuidv4 } from 'uuid';
import { otsApi } from '../lib/api';
import { db } from '../lib/db';
import { compressImage, compressSignature } from '../lib/image-compression';
import { mapRemoteOtToDb } from '../lib/offline-ots';
import { syncEngine } from '../lib/sync-engine';

export interface CreateOTDto {
  clienteTarjeta: string;
  vehiculoId?: number;
  vehiculo?: {
    placa: string;
    marca?: string;
    modelo?: string;
    anio?: number;
    motor?: string;
    cilindraje?: string;
    color?: string;
    tipoVehiculoId?: number;
    clienteId?: string;
    clienteIds?: string[];
  };
  fechaEntrada: string;
  km: number;
  nivelGasolina?: number;
  requerimientoCliente?: string;
  observaciones?: string;
  cosasTrae?: number[];
}

export interface OTPhoto {
  file: File;
  tipo: 'general' | 'dano' | 'documento';
  descripcion?: string;
}

export interface OTDamage {
  tipoDanoId: number;
  zona: string;
  descripcion?: string;
  posicionX?: number;
  posicionY?: number;
  foto?: File;
}

export interface OTFirmas {
  firmaCliente?: string;
  nombreFirmaCliente?: string;
  firmaRecepcion?: string;
  nombreFirmaRecepcion?: string;
}

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 message = String(error?.message || '');
  return /Failed to fetch|NetworkError|ERR_EMPTY_RESPONSE|ECONNREFUSED|ECONNRESET|Internal server error/i.test(message);
}

async function createOTOffline(
  data: CreateOTDto,
  options?: {
    fotos?: OTPhoto[];
    danos?: OTDamage[];
    firmas?: OTFirmas;
    pdfEscaner?: File;
  }
): Promise<{ localId: string; serverId?: number; numeroOt?: string; routeId: string }> {
  const localId = uuidv4();
  const numeroOtLocal = `OT-LOCAL-${localId.slice(0, 8).toUpperCase()}`;

  await db.ordenesTrabajo.add({
    localId,
    numeroOt: numeroOtLocal,
    clienteTarjeta: data.clienteTarjeta,
    vehiculo: data.vehiculo ?? (data.vehiculoId ? { id: data.vehiculoId } : {}),
    fechaEntrada: new Date(data.fechaEntrada),
    km: data.km,
    nivelGasolina: data.nivelGasolina ?? 0,
    requerimientoCliente: data.requerimientoCliente,
    observaciones: data.observaciones,
    estado: 'borrador',
    cosasTrae: data.cosasTrae ?? [],
    danos: [],
    fotos: [],
    syncStatus: 'pending',
    lastModified: Date.now(),
    version: 1,
  });

  await syncEngine.queueEvent({
    eventId: uuidv4(),
    eventType: 'CREATE_OT',
    entityType: 'ot',
    localId,
    payload: {
      ...data,
      localId,
    },
  });

  if (options?.fotos && options.fotos.length > 0) {
    for (const foto of options.fotos) {
      await addOTPhoto(localId, foto);
    }
  }

  if (options?.danos && options.danos.length > 0) {
    for (const dano of options.danos) {
      await addOTDamage(localId, dano);
    }
  }

  if (options?.firmas) {
    await setOTFirmas(localId, options.firmas);
  }

  if (options?.pdfEscaner) {
    await uploadPDF(localId, options.pdfEscaner);
  }

  return { localId, numeroOt: numeroOtLocal, routeId: `local-${localId}` };
}

export async function createOT(
  data: CreateOTDto,
  options?: {
    fotos?: OTPhoto[];
    danos?: OTDamage[];
    firmas?: OTFirmas;
    pdfEscaner?: File;
  }
): Promise<{ localId: string; serverId?: number; numeroOt?: string; routeId: string }> {
  const hasDeferredAssets = Boolean(
    options?.fotos?.length || options?.danos?.length || options?.firmas || options?.pdfEscaner,
  );

  if (typeof navigator !== 'undefined' && navigator.onLine && !hasDeferredAssets) {
    try {
      const created = await otsApi.create(data);
      const mapped = mapRemoteOtToDb(created);
      await db.ordenesTrabajo.put(mapped);

      return {
        localId: mapped.localId,
        serverId: Number(created.id),
        numeroOt: created.numeroOt,
        routeId: String(created.id),
      };
    } catch (error: any) {
      if (!isNetworkLikeError(error)) {
        throw error;
      }
    }
  }

  return createOTOffline(data, options);
}

async function addOTPhoto(localId: string, foto: OTPhoto): Promise<void> {
  const compressed = await compressImage(foto.file, {
    maxWidth: 1920,
    quality: 0.8,
    outputFormat: 'webp',
  });

  const fotoId = uuidv4();
  await db.otFotos.add({
    id: fotoId,
    otLocalId: localId,
    tipo: foto.tipo,
    descripcion: foto.descripcion,
    dataUrl: compressed.dataUrl,
    synced: false,
  });

  await syncEngine.queueEvent({
    eventId: uuidv4(),
    eventType: 'ADD_OT_PHOTO',
    entityType: 'ot_foto',
    localId: fotoId,
    payload: {
      otLocalId: localId,
      tipo: foto.tipo,
      descripcion: foto.descripcion,
    },
  });
}

async function addOTDamage(localId: string, dano: OTDamage): Promise<void> {
  const danoId = uuidv4();

  let fotoUrl: string | undefined;

  if (dano.foto) {
    const compressed = await compressImage(dano.foto, {
      maxWidth: 800,
      quality: 0.75,
      outputFormat: 'webp',
    });

    fotoUrl = compressed.dataUrl;
  }

  await db.otDanos.add({
    id: danoId,
    otLocalId: localId,
    tipoDanoId: dano.tipoDanoId,
    zona: dano.zona,
    descripcion: dano.descripcion,
    posicionX: dano.posicionX,
    posicionY: dano.posicionY,
    fotoUrl,
    synced: false,
  });

  await syncEngine.queueEvent({
    eventId: uuidv4(),
    eventType: 'ADD_OT_DAMAGE',
    entityType: 'ot_dano',
    localId: danoId,
    payload: {
      otLocalId: localId,
      tipoDanoId: dano.tipoDanoId,
      zona: dano.zona,
      descripcion: dano.descripcion,
      posicionX: dano.posicionX,
      posicionY: dano.posicionY,
      fotoUrl,
    },
  });
}

async function setOTFirmas(localId: string, firmas: OTFirmas): Promise<void> {
  let firmaClienteUrl: string | undefined;
  let firmaRecepcionUrl: string | undefined;

  if (firmas.firmaCliente) {
    const blob = await compressSignature(firmas.firmaCliente);
    const reader = new FileReader();
    firmaClienteUrl = await new Promise((resolve) => {
      reader.onload = () => resolve(reader.result as string);
      reader.readAsDataURL(blob);
    });
  }

  if (firmas.firmaRecepcion) {
    const blob = await compressSignature(firmas.firmaRecepcion);
    const reader = new FileReader();
    firmaRecepcionUrl = await new Promise((resolve) => {
      reader.onload = () => resolve(reader.result as string);
      reader.readAsDataURL(blob);
    });
  }

  await syncEngine.queueEvent({
    eventId: uuidv4(),
    eventType: 'SET_OT_FIRMAS',
    entityType: 'ot_firma',
    localId,
    payload: {
      otLocalId: localId,
      firmaCliente: firmaClienteUrl,
      nombreFirmaCliente: firmas.nombreFirmaCliente,
      firmaRecepcion: firmaRecepcionUrl,
      nombreFirmaRecepcion: firmas.nombreFirmaRecepcion,
    },
  });
}

async function uploadPDF(localId: string, pdf: File): Promise<void> {
  void pdf;
  await syncEngine.queueEvent({
    eventId: uuidv4(),
    eventType: 'UPLOAD_PDF',
    entityType: 'ot',
    localId,
    payload: {
      otLocalId: localId,
    },
  });
}

export async function getOTByLocalId(localId: string) {
  return db.ordenesTrabajo.where('localId').equals(localId).first();
}

export async function getAllOTs() {
  return db.ordenesTrabajo.toArray();
}
