package com.ejie.ad74a.util.comun;

import java.math.BigDecimal;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ejie.ad74a.exceptions.Mensaje;
import com.ejie.ad74a.util.constantes.modelos.TipoCuentas;

/**
 * UtilValidaciones
 * 
 * @author GFI
 *
 */
public final class UtilValidaciones {

	private static final Logger LOG = LoggerFactory.getLogger(UtilValidaciones.class);

	public static final String LABEL_ERROR_OBLIGATORIO = "error.obligatorio";
	public static final String LABEL_ERROR_NUMERICO = "error.numerico";
	public static final String LABEL_ERROR_RANGO = "error.rango";
	public static final String LABEL_ERROR_RANGO_MIN_MAX = "error.rangoMinMax";
	public static final String LABEL_ERROR_LONGITUD = "error.longitud";
	public static final String LABEL_ERROR_LONGITUD_EXACTA = "error.longitudExacta";
	public static final String LABEL_ERROR_DUPLICADO = "error.campo.duplicado";
	public static final String LABEL_ERROR_NUMERO_MAXIMO = "error.numero.maximo";
	public static final String LABEL_ERROR_NUMERO_MAXIMO_DECIMALES = "error.numero.maximo.decimales";
	public static final String LABEL_ERROR_FECHAS = "error.fecha.vigencia";
	public static final String LABEL_ERROR_NIF_NIE = "error.nifNie";
	public static final String LABEL_IDENTIFICADOR_NULL = "cuentas.tipoIdentificadorNull";
	public static final String LABEL_ERROR_FORMATO_EMAIL = "error.formato.email";
	public static final String LABEL_ERROR_FORMATO = "error.formato";
	public static final String LABEL_ERROR_FECHA_POSTERIOR_HOY = "error.fecha.posterior.hoy";
	public static final String LABEL_ERROR_EXCLUYENTES = "error.excluyentes";
	public static final String LABEL_ERROR_IBAN = "error.iban";

	/**
	 * Constructor privado sin parametros
	 */
	private UtilValidaciones() {
	}

	/**
	 * Devuelve el mensaje de validación de longitud
	 * 
	 * @param longitud    int Longitud del campo
	 * @param longitudMax int Longitud del campo permitida
	 * @return Mensaje creado
	 */
	public static Mensaje getMensajeValidaLongitud(int longitud, int longitudMax) {
		return new Mensaje(LABEL_ERROR_LONGITUD, false, String.valueOf(longitud), String.valueOf(longitudMax));
	}

	/**
	 * Devuelve el mensaje de validación de longitud exacta
	 * 
	 * @param longitud    int de longitud del campo
	 * @param longitudMax int de longitud necesaria del campo
	 * @return Mensaje creado
	 */
	public static Mensaje getMensajeValidaLongitudExacta(int longitud, int longitudMax) {
		return new Mensaje(LABEL_ERROR_LONGITUD_EXACTA, false, String.valueOf(longitud), String.valueOf(longitudMax));
	}

	/**
	 * Devuelve el mensaje de validación de un número
	 * 
	 * @param numero    numero a validar
	 * @param numeroMax tamaño máximo del número
	 * @return Mensaje creado
	 */
	public static Mensaje getMensajeValidaValorNumero(Long numero, Long numeroMax) {
		return new Mensaje(LABEL_ERROR_NUMERO_MAXIMO, false, String.valueOf(numero), String.valueOf(numeroMax));
	}

	/**
	 * Devuelve el mensaje de validación de un número
	 * 
	 * @param numero    numero a validar
	 * @param numeroMax tamaño máximo del número
	 * @return Mensaje creado
	 */
	public static Mensaje getMensajeValidaValorNumero(BigDecimal numero, BigDecimal numeroMax) {
		return new Mensaje(LABEL_ERROR_NUMERO_MAXIMO, false, String.valueOf(numero), String.valueOf(numeroMax));
	}

	/**
	 * Devuelve el mensaje de validación de un número de decimales
	 * 
	 * @param numero    numero a validar
	 * @param numeroMax tamaño máximo del número de decimales
	 * @return Mensaje creado
	 */
	public static Mensaje getMensajeValidaValorNumeroDecimales(int numero, int numeroMax) {
		return new Mensaje(LABEL_ERROR_NUMERO_MAXIMO_DECIMALES, false, String.valueOf(numero),
				String.valueOf(numeroMax));
	}

	/**
	 * Devuelve el mensaje de validación de dos fechas
	 * 
	 * @param fechaIni fecha de inicio
	 * @param fechaFin fecha fin
	 * @return Mensaje creado
	 */
	public static Mensaje getMensajeValidaFecha(String fechaIni, String fechaFin) {
		return new Mensaje(LABEL_ERROR_FECHAS, false, fechaIni, fechaFin);
	}

	/**
	 * Devuelve el mensaje de validación de formato
	 * 
	 * @param campo string campo
	 * @return Mensaje creado
	 */
	public static Mensaje getMensajeValidaFormato(String campo) {
		return new Mensaje(LABEL_ERROR_FORMATO, false, campo);
	}

	/**
	 * Metodo para la validacion de un IBAN.
	 * 
	 * @param iban String
	 * @return boolean
	 */
	public static boolean isIBANCorrecto(String iban) {

		if (Utilidades.esBlancoNulo(iban)) {
			return false;
		} else if (iban.length() != TipoCuentas.MAX_LENGTH_NUMCUENTA05) {
			return false;
		}

		String reordenado = iban.substring(4) + iban.substring(0, 4);
		String convertido = reordenado;
		for (char c : reordenado.toCharArray()) {
			if (!Character.isDigit(c)) {
				convertido = convertido.replaceAll(String.valueOf(c), String.valueOf((Character.getNumericValue(c))));
			}
		}

		BigDecimal num = new BigDecimal(convertido);

		if (num.remainder(new BigDecimal("97")).equals(BigDecimal.ONE)) {
			return true;
		} else {
			return false;
		}

	}

	// Constantes isValidCif
	private static final int TAM_CIF = 9;
	private static final Pattern PATTERN_CIF = Pattern.compile("[[A-H][J-N][P-S]UVW][0-9]{7}[0-9A-J]");
	private static final String CONTROL_SOLO_NUMEROS = "ABEH";
	private static final String CONTROL_SOLO_LETRAS = "KPQS";
	private static final String CONTROL_NUMERO_A_LETRA = "JABCDEFGHI";
	private static final int BASE_DIEZ = 10;
	private static final int NUM_DOS = 2;

	/**
	 * Realiza la validacion si la cadena representa un CIF
	 * 
	 * @param str la cadena a comprobar
	 * @return true si la cadena representa un CIF del tipo indicado
	 */
	public static boolean isValidCif(String str) {
		UtilValidaciones.LOG.trace("isValidCif {}", str);
		String cif = str.toUpperCase();
		try {
			if (!UtilValidaciones.PATTERN_CIF.matcher(cif).matches()) {
				// No cumple el patrón
				return false;
			}
			// Sumamos los dígitos pares
			int parA = 0;
			for (int i = NUM_DOS; i < (UtilValidaciones.TAM_CIF - 1); i += NUM_DOS) {
				final int digito = Character.digit(cif.charAt(i), 10);
				if (digito < 0) {
					return false;
				}
				parA += digito;
			}
			// Para cada uno de los dígitos de la posiciones impares,
			// multiplicarlo por 2 y sumar los dígitos del resultado
			int nonB = 0;
			for (int i = 1; i < UtilValidaciones.TAM_CIF; i += NUM_DOS) {
				final int digito = Character.digit(cif.charAt(i), 10);
				if (digito < 0) {
					return false;
				}
				int nn = NUM_DOS * digito;
				if (nn >= UtilValidaciones.BASE_DIEZ) {
					nn = 1 + (nn - UtilValidaciones.BASE_DIEZ);
				}
				nonB += nn;
			}
			// Sumar pares mas impares
			final int parcialC = parA + nonB;
			// Obtener solo el último digito de parcialC, obteniendo digitoE
			final int digitoE = parcialC % UtilValidaciones.BASE_DIEZ;
			// Si digitoE es mayor que 0 restar el valor de digitoE a 10. Si
			// digitoE es = 0 nos quedamos con el 0
			final int digitoD = (digitoE > 0) ? (UtilValidaciones.BASE_DIEZ - digitoE) : 0;
			final char letraIni = cif.charAt(0);
			final char caracterFin = cif.charAt(8);
			// El último dígito (posición 9) es un código de control que puede
			// ser un número o una letra:
			// Será una LETRA si la clave de entidad es K, P, Q ó S.
			// Será un NUMERO si la entidad es A, B, E ó H.
			// Para otras claves de entidad: el dígito podrá ser tanto número
			// como letra.
			return
			// ¿el caracter de control es válido como letra?
			(UtilValidaciones.CONTROL_SOLO_NUMEROS.indexOf(letraIni) < 0
					&& UtilValidaciones.CONTROL_NUMERO_A_LETRA.charAt(digitoD) == caracterFin) ||
			// ¿el caracter de control es válido como dígito?
					(UtilValidaciones.CONTROL_SOLO_LETRAS.indexOf(letraIni) < 0
							&& digitoD == Character.digit(caracterFin, UtilValidaciones.BASE_DIEZ));
		} catch (Exception e) {
			return false;
		}
	}

}
