import { Repository, DataSource, Between, MoreThanOrEqual, LessThanOrEqual } from 'typeorm';
import { InspeccionPreViaje, ChecklistItem, FotoInspeccion } from '../entities';

/**
 * DTO para crear inspeccion pre-viaje
 */
export interface CreateInspeccionDto {
  viajeId: string;
  remolqueId?: string;
  fechaInspeccion?: Date;
  checklistItems?: ChecklistItem[];
  fotos?: FotoInspeccion[];
}

/**
 * DTO para actualizar inspeccion
 */
export interface UpdateInspeccionDto {
  checklistItems?: ChecklistItem[];
  fotos?: FotoInspeccion[];
  firmaOperador?: string;
}

/**
 * Rango de fechas para busquedas
 */
export interface DateRange {
  desde: Date;
  hasta: Date;
}

/**
 * Resumen de defectos
 */
export interface ResumenDefectos {
  totalItems: number;
  itemsOk: number;
  defectosMenores: number;
  defectosCriticos: number;
  noAplica: number;
  porcentajeAprobacion: number;
  defectosDetallados: {
    item: string;
    categoria: string;
    estado: string;
    observacion?: string;
  }[];
}

/**
 * Resultado de verificacion de despacho
 */
export interface ResultadoDespacho {
  puedeDespachar: boolean;
  unidadId: string;
  ultimaInspeccion: InspeccionPreViaje | null;
  horasDesdeUltimaInspeccion: number | null;
  defectosCriticosPendientes: number;
  requiereNuevaInspeccion: boolean;
  motivos: string[];
}

/**
 * Categorias de checklist predefinidas
 */
export const CATEGORIAS_CHECKLIST = {
  MOTOR: 'Motor',
  FRENOS: 'Frenos',
  LLANTAS: 'Llantas',
  LUCES: 'Luces',
  SUSPENSION: 'Suspension',
  CABINA: 'Cabina',
  CARROCERIA: 'Carroceria',
  DOCUMENTOS: 'Documentos',
  SEGURIDAD: 'Seguridad',
  REMOLQUE: 'Remolque',
} as const;

/**
 * Servicio para gestion de inspecciones pre-viaje
 * NOM-087-SCT-2-2017 Compliance
 */
export class InspeccionPreViajeService {
  private inspeccionRepository: Repository<InspeccionPreViaje>;

  // Horas maximas desde ultima inspeccion para permitir despacho
  private readonly HORAS_VALIDEZ_INSPECCION = 24;

  constructor(private readonly dataSource: DataSource) {
    this.inspeccionRepository = dataSource.getRepository(InspeccionPreViaje);
  }

  /**
   * Crea una nueva inspeccion pre-viaje
   */
  async create(
    tenantId: string,
    unidadId: string,
    operadorId: string,
    data: CreateInspeccionDto
  ): Promise<InspeccionPreViaje> {
    // Validar que no exista inspeccion en progreso para esta unidad
    const inspeccionEnProgreso = await this.findInspeccionEnProgreso(
      tenantId,
      unidadId
    );

    if (inspeccionEnProgreso) {
      throw new Error(
        `Ya existe una inspeccion en progreso para la unidad. ID: ${inspeccionEnProgreso.id}`
      );
    }

    const checklistItems = data.checklistItems || this.getChecklistDefault();

    const inspeccion = this.inspeccionRepository.create({
      tenantId,
      viajeId: data.viajeId,
      unidadId,
      remolqueId: data.remolqueId || null,
      operadorId,
      fechaInspeccion: data.fechaInspeccion || new Date(),
      aprobada: false,
      checklistItems,
      defectosEncontrados: null,
      defectosCriticos: 0,
      defectosMenores: 0,
      firmaOperador: null,
      firmaFecha: null,
      fotos: data.fotos || null,
    });

    return this.inspeccionRepository.save(inspeccion);
  }

  /**
   * Obtiene una inspeccion por ID
   */
  async findById(
    tenantId: string,
    id: string
  ): Promise<InspeccionPreViaje | null> {
    return this.inspeccionRepository.findOne({
      where: { id, tenantId },
    });
  }

  /**
   * Obtiene inspecciones por unidad en un rango de fechas
   */
  async findByUnidad(
    tenantId: string,
    unidadId: string,
    dateRange?: DateRange
  ): Promise<InspeccionPreViaje[]> {
    const where: any = { tenantId, unidadId };

    if (dateRange) {
      where.fechaInspeccion = Between(dateRange.desde, dateRange.hasta);
    }

    return this.inspeccionRepository.find({
      where,
      order: { fechaInspeccion: 'DESC' },
    });
  }

  /**
   * Obtiene inspecciones por operador en un rango de fechas
   */
  async findByOperador(
    tenantId: string,
    operadorId: string,
    dateRange?: DateRange
  ): Promise<InspeccionPreViaje[]> {
    const where: any = { tenantId, operadorId };

    if (dateRange) {
      where.fechaInspeccion = Between(dateRange.desde, dateRange.hasta);
    }

    return this.inspeccionRepository.find({
      where,
      order: { fechaInspeccion: 'DESC' },
    });
  }

  /**
   * Obtiene inspecciones por viaje
   */
  async findByViaje(
    tenantId: string,
    viajeId: string
  ): Promise<InspeccionPreViaje[]> {
    return this.inspeccionRepository.find({
      where: { tenantId, viajeId },
      order: { fechaInspeccion: 'DESC' },
    });
  }

  /**
   * Agrega o actualiza un item del checklist
   */
  async addChecklistItem(
    tenantId: string,
    inspeccionId: string,
    item: ChecklistItem
  ): Promise<InspeccionPreViaje | null> {
    const inspeccion = await this.findById(tenantId, inspeccionId);
    if (!inspeccion) return null;

    // No permitir modificar inspecciones ya completadas
    if (inspeccion.aprobada || inspeccion.firmaOperador) {
      throw new Error('No se puede modificar una inspeccion completada');
    }

    // Validar item
    this.validateChecklistItem(item);

    // Buscar si el item ya existe
    const existingIndex = inspeccion.checklistItems.findIndex(
      i => i.item === item.item && i.categoria === item.categoria
    );

    if (existingIndex >= 0) {
      // Actualizar item existente
      inspeccion.checklistItems[existingIndex] = item;
    } else {
      // Agregar nuevo item
      inspeccion.checklistItems.push(item);
    }

    // Recalcular conteos de defectos
    this.recalcularDefectos(inspeccion);

    return this.inspeccionRepository.save(inspeccion);
  }

  /**
   * Actualiza multiples items del checklist
   */
  async updateChecklistItems(
    tenantId: string,
    inspeccionId: string,
    items: ChecklistItem[]
  ): Promise<InspeccionPreViaje | null> {
    const inspeccion = await this.findById(tenantId, inspeccionId);
    if (!inspeccion) return null;

    if (inspeccion.aprobada || inspeccion.firmaOperador) {
      throw new Error('No se puede modificar una inspeccion completada');
    }

    // Validar todos los items
    for (const item of items) {
      this.validateChecklistItem(item);
    }

    inspeccion.checklistItems = items;
    this.recalcularDefectos(inspeccion);

    return this.inspeccionRepository.save(inspeccion);
  }

  /**
   * Completa una inspeccion y la marca como aprobada o rechazada
   */
  async completeInspeccion(
    tenantId: string,
    id: string,
    firmaOperador?: string
  ): Promise<InspeccionPreViaje | null> {
    const inspeccion = await this.findById(tenantId, id);
    if (!inspeccion) return null;

    if (inspeccion.aprobada || inspeccion.firmaOperador) {
      throw new Error('La inspeccion ya fue completada');
    }

    // Recalcular defectos finales
    this.recalcularDefectos(inspeccion);

    // Determinar si aprobada (sin defectos criticos)
    inspeccion.aprobada = inspeccion.defectosCriticos === 0;

    // Registrar firma y fecha
    if (firmaOperador) {
      inspeccion.firmaOperador = firmaOperador;
      inspeccion.firmaFecha = new Date();
    }

    // Generar lista de defectos encontrados
    const defectos = inspeccion.checklistItems
      .filter(i => i.estado === 'DEFECTO_MENOR' || i.estado === 'DEFECTO_CRITICO')
      .map(i => `[${i.estado}] ${i.categoria}: ${i.item}${i.observacion ? ` - ${i.observacion}` : ''}`);

    inspeccion.defectosEncontrados = defectos.length > 0 ? defectos : null;

    return this.inspeccionRepository.save(inspeccion);
  }

  /**
   * Obtiene los defectos encontrados en una inspeccion
   */
  async getDefectos(
    tenantId: string,
    id: string
  ): Promise<ResumenDefectos | null> {
    const inspeccion = await this.findById(tenantId, id);
    if (!inspeccion) return null;

    const items = inspeccion.checklistItems;
    const itemsOk = items.filter(i => i.estado === 'OK').length;
    const defectosMenores = items.filter(i => i.estado === 'DEFECTO_MENOR').length;
    const defectosCriticos = items.filter(i => i.estado === 'DEFECTO_CRITICO').length;
    const noAplica = items.filter(i => i.estado === 'NO_APLICA').length;

    const itemsEvaluados = items.length - noAplica;
    const porcentajeAprobacion = itemsEvaluados > 0
      ? (itemsOk / itemsEvaluados) * 100
      : 0;

    const defectosDetallados = items
      .filter(i => i.estado === 'DEFECTO_MENOR' || i.estado === 'DEFECTO_CRITICO')
      .map(i => ({
        item: i.item,
        categoria: i.categoria,
        estado: i.estado,
        observacion: i.observacion,
      }));

    return {
      totalItems: items.length,
      itemsOk,
      defectosMenores,
      defectosCriticos,
      noAplica,
      porcentajeAprobacion,
      defectosDetallados,
    };
  }

  /**
   * Verifica si una unidad puede ser despachada
   */
  async canDespachar(
    tenantId: string,
    unidadId: string
  ): Promise<ResultadoDespacho> {
    const motivos: string[] = [];

    // Obtener ultima inspeccion de la unidad
    const inspecciones = await this.findByUnidad(tenantId, unidadId);
    const ultimaInspeccion = inspecciones.length > 0 ? inspecciones[0] : null;

    // Calcular horas desde ultima inspeccion
    let horasDesdeUltimaInspeccion: number | null = null;
    let requiereNuevaInspeccion = true;

    if (ultimaInspeccion) {
      const ahora = new Date();
      const fechaInspeccion = new Date(ultimaInspeccion.fechaInspeccion);
      horasDesdeUltimaInspeccion =
        (ahora.getTime() - fechaInspeccion.getTime()) / (1000 * 60 * 60);

      requiereNuevaInspeccion = horasDesdeUltimaInspeccion > this.HORAS_VALIDEZ_INSPECCION;
    }

    // Verificar si hay defectos criticos pendientes
    let defectosCriticosPendientes = 0;

    if (ultimaInspeccion) {
      defectosCriticosPendientes = ultimaInspeccion.defectosCriticos;
    }

    // Determinar si puede despachar
    let puedeDespachar = true;

    if (!ultimaInspeccion) {
      puedeDespachar = false;
      motivos.push('No existe inspeccion pre-viaje para esta unidad');
    } else {
      if (!ultimaInspeccion.aprobada) {
        puedeDespachar = false;
        motivos.push('La ultima inspeccion no fue aprobada');
      }

      if (defectosCriticosPendientes > 0) {
        puedeDespachar = false;
        motivos.push(`Existen ${defectosCriticosPendientes} defectos criticos sin resolver`);
      }

      if (requiereNuevaInspeccion) {
        puedeDespachar = false;
        motivos.push(
          `Han pasado mas de ${this.HORAS_VALIDEZ_INSPECCION} horas desde la ultima inspeccion`
        );
      }

      if (!ultimaInspeccion.firmaOperador) {
        puedeDespachar = false;
        motivos.push('La inspeccion no cuenta con firma del operador');
      }
    }

    return {
      puedeDespachar,
      unidadId,
      ultimaInspeccion,
      horasDesdeUltimaInspeccion,
      defectosCriticosPendientes,
      requiereNuevaInspeccion,
      motivos,
    };
  }

  /**
   * Obtiene la ultima inspeccion aprobada de una unidad
   */
  async getUltimaInspeccionAprobada(
    tenantId: string,
    unidadId: string
  ): Promise<InspeccionPreViaje | null> {
    return this.inspeccionRepository.findOne({
      where: { tenantId, unidadId, aprobada: true },
      order: { fechaInspeccion: 'DESC' },
    });
  }

  /**
   * Agrega fotos a una inspeccion
   */
  async addFotos(
    tenantId: string,
    inspeccionId: string,
    fotos: FotoInspeccion[]
  ): Promise<InspeccionPreViaje | null> {
    const inspeccion = await this.findById(tenantId, inspeccionId);
    if (!inspeccion) return null;

    const fotosActuales = inspeccion.fotos || [];
    inspeccion.fotos = [...fotosActuales, ...fotos];

    return this.inspeccionRepository.save(inspeccion);
  }

  /**
   * Obtiene estadisticas de inspecciones por periodo
   */
  async getEstadisticas(
    tenantId: string,
    dateRange: DateRange
  ): Promise<{
    totalInspecciones: number;
    aprobadas: number;
    rechazadas: number;
    porcentajeAprobacion: number;
    defectosMasComunes: { item: string; count: number }[];
  }> {
    const inspecciones = await this.inspeccionRepository.find({
      where: {
        tenantId,
        fechaInspeccion: Between(dateRange.desde, dateRange.hasta),
      },
    });

    const aprobadas = inspecciones.filter(i => i.aprobada).length;
    const rechazadas = inspecciones.length - aprobadas;

    // Contar defectos mas comunes
    const defectosCount: Record<string, number> = {};
    for (const insp of inspecciones) {
      for (const item of insp.checklistItems) {
        if (item.estado === 'DEFECTO_MENOR' || item.estado === 'DEFECTO_CRITICO') {
          const key = `${item.categoria}: ${item.item}`;
          defectosCount[key] = (defectosCount[key] || 0) + 1;
        }
      }
    }

    const defectosMasComunes = Object.entries(defectosCount)
      .map(([item, count]) => ({ item, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 10);

    return {
      totalInspecciones: inspecciones.length,
      aprobadas,
      rechazadas,
      porcentajeAprobacion: inspecciones.length > 0
        ? (aprobadas / inspecciones.length) * 100
        : 0,
      defectosMasComunes,
    };
  }

  /**
   * Busca inspeccion en progreso (sin completar) para una unidad
   */
  private async findInspeccionEnProgreso(
    tenantId: string,
    unidadId: string
  ): Promise<InspeccionPreViaje | null> {
    // Buscar inspecciones de las ultimas 24 horas sin firma
    const hace24Horas = new Date();
    hace24Horas.setHours(hace24Horas.getHours() - 24);

    return this.inspeccionRepository.findOne({
      where: {
        tenantId,
        unidadId,
        firmaOperador: undefined,
        fechaInspeccion: MoreThanOrEqual(hace24Horas),
      },
      order: { fechaInspeccion: 'DESC' },
    });
  }

  /**
   * Valida un item del checklist
   */
  private validateChecklistItem(item: ChecklistItem): void {
    const errors: string[] = [];

    if (!item.item || item.item.trim().length === 0) {
      errors.push('item es requerido');
    }

    if (!item.categoria || item.categoria.trim().length === 0) {
      errors.push('categoria es requerida');
    }

    const estadosValidos = ['OK', 'DEFECTO_MENOR', 'DEFECTO_CRITICO', 'NO_APLICA'];
    if (!estadosValidos.includes(item.estado)) {
      errors.push(`estado debe ser uno de: ${estadosValidos.join(', ')}`);
    }

    if (errors.length > 0) {
      throw new Error(`Item de checklist invalido: ${errors.join(', ')}`);
    }
  }

  /**
   * Recalcula contadores de defectos
   */
  private recalcularDefectos(inspeccion: InspeccionPreViaje): void {
    const items = inspeccion.checklistItems;

    inspeccion.defectosCriticos = items.filter(
      i => i.estado === 'DEFECTO_CRITICO'
    ).length;

    inspeccion.defectosMenores = items.filter(
      i => i.estado === 'DEFECTO_MENOR'
    ).length;
  }

  /**
   * Obtiene checklist por defecto segun NOM-087
   */
  private getChecklistDefault(): ChecklistItem[] {
    return [
      // Motor
      { item: 'Nivel de aceite de motor', categoria: CATEGORIAS_CHECKLIST.MOTOR, estado: 'OK' },
      { item: 'Nivel de refrigerante', categoria: CATEGORIAS_CHECKLIST.MOTOR, estado: 'OK' },
      { item: 'Fugas visibles', categoria: CATEGORIAS_CHECKLIST.MOTOR, estado: 'OK' },
      { item: 'Bandas y mangueras', categoria: CATEGORIAS_CHECKLIST.MOTOR, estado: 'OK' },

      // Frenos
      { item: 'Presion de aire en sistema', categoria: CATEGORIAS_CHECKLIST.FRENOS, estado: 'OK' },
      { item: 'Funcionamiento freno de servicio', categoria: CATEGORIAS_CHECKLIST.FRENOS, estado: 'OK' },
      { item: 'Funcionamiento freno de estacionamiento', categoria: CATEGORIAS_CHECKLIST.FRENOS, estado: 'OK' },
      { item: 'Ajuste de frenos', categoria: CATEGORIAS_CHECKLIST.FRENOS, estado: 'OK' },

      // Llantas
      { item: 'Presion de inflado', categoria: CATEGORIAS_CHECKLIST.LLANTAS, estado: 'OK' },
      { item: 'Profundidad de dibujo', categoria: CATEGORIAS_CHECKLIST.LLANTAS, estado: 'OK' },
      { item: 'Danos visibles', categoria: CATEGORIAS_CHECKLIST.LLANTAS, estado: 'OK' },
      { item: 'Birlos y tuercas', categoria: CATEGORIAS_CHECKLIST.LLANTAS, estado: 'OK' },

      // Luces
      { item: 'Luces frontales', categoria: CATEGORIAS_CHECKLIST.LUCES, estado: 'OK' },
      { item: 'Luces traseras', categoria: CATEGORIAS_CHECKLIST.LUCES, estado: 'OK' },
      { item: 'Direccionales', categoria: CATEGORIAS_CHECKLIST.LUCES, estado: 'OK' },
      { item: 'Luces de freno', categoria: CATEGORIAS_CHECKLIST.LUCES, estado: 'OK' },
      { item: 'Luces de galibo', categoria: CATEGORIAS_CHECKLIST.LUCES, estado: 'OK' },

      // Suspension
      { item: 'Amortiguadores', categoria: CATEGORIAS_CHECKLIST.SUSPENSION, estado: 'OK' },
      { item: 'Muelles/bolsas de aire', categoria: CATEGORIAS_CHECKLIST.SUSPENSION, estado: 'OK' },
      { item: 'Quinta rueda', categoria: CATEGORIAS_CHECKLIST.SUSPENSION, estado: 'OK' },

      // Cabina
      { item: 'Espejos retrovisores', categoria: CATEGORIAS_CHECKLIST.CABINA, estado: 'OK' },
      { item: 'Parabrisas', categoria: CATEGORIAS_CHECKLIST.CABINA, estado: 'OK' },
      { item: 'Limpiadores', categoria: CATEGORIAS_CHECKLIST.CABINA, estado: 'OK' },
      { item: 'Cinturones de seguridad', categoria: CATEGORIAS_CHECKLIST.CABINA, estado: 'OK' },
      { item: 'Claxon', categoria: CATEGORIAS_CHECKLIST.CABINA, estado: 'OK' },
      { item: 'Instrumentos tablero', categoria: CATEGORIAS_CHECKLIST.CABINA, estado: 'OK' },

      // Seguridad
      { item: 'Extintor', categoria: CATEGORIAS_CHECKLIST.SEGURIDAD, estado: 'OK' },
      { item: 'Triangulos reflectantes', categoria: CATEGORIAS_CHECKLIST.SEGURIDAD, estado: 'OK' },
      { item: 'Botiquin', categoria: CATEGORIAS_CHECKLIST.SEGURIDAD, estado: 'OK' },
      { item: 'Calzas', categoria: CATEGORIAS_CHECKLIST.SEGURIDAD, estado: 'OK' },

      // Documentos
      { item: 'Licencia vigente', categoria: CATEGORIAS_CHECKLIST.DOCUMENTOS, estado: 'OK' },
      { item: 'Tarjeta de circulacion', categoria: CATEGORIAS_CHECKLIST.DOCUMENTOS, estado: 'OK' },
      { item: 'Poliza de seguro', categoria: CATEGORIAS_CHECKLIST.DOCUMENTOS, estado: 'OK' },
      { item: 'Permiso SCT', categoria: CATEGORIAS_CHECKLIST.DOCUMENTOS, estado: 'OK' },
    ];
  }
}
