package com.dacrt.SBIABackend.security.controler;

import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.dacrt.SBIABackend.security.dto.AuditRequestDto;
import com.dacrt.SBIABackend.security.dto.ParamsDto;
import com.dacrt.SBIABackend.security.dto.PrivileResponseDto;
import com.dacrt.SBIABackend.security.dto.PrivilegeUniqueResponseDto;
import com.dacrt.SBIABackend.security.dto.PrivilegesAllDto;
import com.dacrt.SBIABackend.security.dto.PrivilegesListDto;
import com.dacrt.SBIABackend.security.dto.PrivilegesResponseDto;
import com.dacrt.SBIABackend.security.dto.RespuestaMsgDto;
import com.dacrt.SBIABackend.security.dto.RolAccesoDto;
import com.dacrt.SBIABackend.security.dto.RolAssinedRequestDto;
import com.dacrt.SBIABackend.security.dto.RolPrivilegioAssignedResponseDto;
import com.dacrt.SBIABackend.security.dto.RoleDto;
import com.dacrt.SBIABackend.security.dto.RolesAssignedResponseDto;
import com.dacrt.SBIABackend.security.dto.RolesResponseDto;
import com.dacrt.SBIABackend.security.dto.UsersListDto;
import com.dacrt.SBIABackend.security.entity.Params;
import com.dacrt.SBIABackend.security.entity.Privileges;
import com.dacrt.SBIABackend.security.entity.Roles;
import com.dacrt.SBIABackend.security.entity.Rolesprivileges;
import com.dacrt.SBIABackend.security.entity.Users;
import com.dacrt.SBIABackend.security.repository.AuditRepository;
import com.dacrt.SBIABackend.security.repository.ParamsRepository;
import com.dacrt.SBIABackend.security.repository.PrivilegesRepository;
import com.dacrt.SBIABackend.security.repository.RolesPrivilegesRepository;
import com.dacrt.SBIABackend.security.repository.RolesRepository;
import com.dacrt.SBIABackend.security.repository.UsersRepository;
import com.dacrt.SBIABackend.security.service.MenuService;

import com.dacrt.SBIABackend.security.service.SecurityService;

import com.dacrt.SBIABackend.security.service.UsersService;
import com.dacrt.SBIABackend.utils.HttpReqRespUtils;

@RestController
//@RequestMapping("/privileges")
@CrossOrigin(origins = "*")
public class PrivilegesController {
	

	@PersistenceContext
	private EntityManager entityManager;

	@Autowired
	private PrivilegesRepository privilegesRepository;

	@Autowired
	private RolesPrivilegesRepository rolesPrivilegesRepository;

	@Autowired
	private ParamsRepository paramsRepository;

	@Autowired
	private RolesRepository rolesRepository;

	@Autowired
	private AuditRepository auditRepository;

	@Autowired
	private UsersRepository usersRepository;

	@Autowired
	MenuService menuService;
	
	
	@Autowired
	SecurityService securityService;


	@Autowired
	UsersService usersService;

	Logger logger = LoggerFactory.getLogger(PrivilegesController.class);

	@PostMapping("/privileges")
	public ResponseEntity<?> listarPrivilegios(HttpServletRequest request, @RequestBody PrivilegesListDto tiposfiltros)
			throws ParseException {

		RespuestaMsgDto respuesta = new RespuestaMsgDto("");
		HttpStatus estatus = HttpStatus.FORBIDDEN;
		long cuantosregistro = 0;

		String sessionid = request.getHeader("Authorization");
		Date fecha = new Date();
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String dataFormattata = formatter.format(fecha);
		// AuditRequestDto auditDto = new AuditRequestDto();

		String searchIn = "";
		String searchModule = "";
		String searchUsr = "";
		String contentIn = "";
		int orderIn = 0;
		int offsetIn = 0;
		int numofrecordsIn = 0;

		Optional<Params> deSessDuration = paramsRepository.findByParamname("SESSION_DURATION");
		String SessionDuration = deSessDuration.get().getValue();
		int duracionSession = Integer.parseInt(SessionDuration);
		Date fecha2 = new Date();
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(fecha2); // tuFechaBase es un Date;
		// calendar.add(Calendar.MINUTE, minutosASumar); //minutosASumar es int.
		calendar.add(Calendar.MINUTE, duracionSession); // horasASumar es int.
		// lo que más quieras sumar
		Date ValidThrufechaSalida = calendar.getTime(); // Y ya tienes la fecha sumada.
		SimpleDateFormat salida = new SimpleDateFormat("yyyy-MM-dd HH:mm"); // 2024-12-23 23:00
		String fechaComoCadena = salida.format(ValidThrufechaSalida);
		System.out.println(fechaComoCadena);

		// Verifico la session
		if (sessionid == null) {
			respuesta.setMsg("Llamada al servicio malformado");
			estatus = HttpStatus.BAD_REQUEST;
			return new ResponseEntity(respuesta, estatus);
		} else {
			sessionid = sessionid.substring(7);
			// verifico si la sesión del usuario existe.
			Optional<Users> encontreSessionUsuario = usersRepository.getBySessionid(sessionid);
			if (encontreSessionUsuario.isPresent()) { // si la sesión del usuario existe
				
				
				//String fechaComoCadena;
				   Date FechaReg = encontreSessionUsuario.get().getValidthru(); 
					   //Llamada a la funcion que validad el tiempo de Session, retorna la fecha sumandole el tiempo de session activa, y vacio si no esta activa
					   
					   // fechaComoCadena  = securityService.consultarSessionActiva(FechaReg,fecha2);
					    fechaComoCadena  = securityService.consultarSessionActiva(FechaReg,fecha2,encontreSessionUsuario.get().getId());
					   
					    if (fechaComoCadena=="") {
						   
						   
					    	RespuestaMsgDto respuestaDto2;
						    String var2 = "";
							boolean bloked2 = false;
							respuestaDto2 = new RespuestaMsgDto(var2);
							//respuestaDto.setBlocked(bloked);
							respuestaDto2.setMsg("Sesión expirada o inválida"); 
							return new ResponseEntity(respuestaDto2, HttpStatus.UNAUTHORIZED);
						   
					    }
					    
				// Obtengo el rol de usuario y verifico si corresponde al privilegio del usuario
				int rolisvalid = 0;
				rolisvalid = auditRepository.getCantbyRolAndPrivi(encontreSessionUsuario.get().getRolid().getId(), 620);
				if (rolisvalid == 0) {
					respuesta.setMsg("No tiene los Privilegios");
					estatus = HttpStatus.FORBIDDEN;
					return new ResponseEntity(respuesta, estatus);
				}

				// Obtengo los datos del Body
				searchIn = tiposfiltros.getFilters().getSearch();// dato de busqueda por nombre o grupo
				searchModule = tiposfiltros.getFilters().getModule();
				searchUsr = tiposfiltros.getFilters().getUser();
				orderIn = tiposfiltros.getOrder(); // Orden Ascedente o Descendente
				offsetIn = tiposfiltros.getOffset();// Primer registro a mostrar
				numofrecordsIn = tiposfiltros.getNumofrecords();// Número de registros a mostrar
				contentIn = tiposfiltros.getContent();
				//Verifico los Shorcouts
				if (contentIn!=null && contentIn!="")
				menuService.iscontentdiffnull(contentIn, encontreSessionUsuario.get().getId());

			} else { // Si la sesión que viene es inválida
				respuesta.setMsg("Sesión expirada o inválida");
				estatus = HttpStatus.BAD_REQUEST;
				return new ResponseEntity(respuesta, estatus);
			}

			// Aqui empieza la carga de la data de respuesta
			List<Privileges> privilegiosMov = new ArrayList<>();
			List<Rolesprivileges> rolesPrivilegiosMov = new ArrayList<>();
			PrivilegesResponseDto privilegesResponseDto = new PrivilegesResponseDto();
			PrivileResponseDto privilegios = new PrivileResponseDto();
			List<PrivileResponseDto> records = new ArrayList<>();
			Optional<Roles> roles;
			RolesAssignedResponseDto rolesAsignned = new RolesAssignedResponseDto();
			List<RolesAssignedResponseDto> ro = new ArrayList<>();
			TypedQuery<RolesAssignedResponseDto> query;
			List<RolesAssignedResponseDto> resultados;
			try {
				// Sentencia General
				String SentenciaBase = "SELECT new com.dacrt.SBIABackend.security.dto.PrivilegeUniqueResponseDto(p.id,p.grp,p.name,p.crud,p.ord) FROM Privileges p";
				//String SentenciaPrivilegios = "SELECT new com.dacrt.SBIABackend.security.dto.RolesAssignedResponseDto(r.id, r.name, CASE WHEN EXISTS (SELECT 1 FROM Rolesprivileges rp2 WHERE rp2.rolid = r.id AND rp2.privilegeid = ? ) THEN TRUE ELSE FALSE END AS estado ) FROM Roles r ";
				String SentenciaPrivilegios = "SELECT new com.dacrt.SBIABackend.security.dto.RolesAssignedResponseDto(r.id, r.name, " +
                        "CASE WHEN EXISTS (SELECT 1 FROM Rolesprivileges rp WHERE rp.rolid = r.id AND rp.privilegeid = :privilegioId) " +
                        "THEN TRUE ELSE FALSE END) " +
                        "FROM Roles r";
				
			 
				
				String QueryTotal = "";
				String name = "p.name"; // buscar por nombre
				String grp = "p.grp"; // buscar por grupo
				String ord = "p.ord"; // buscar por ord
				String lowername = "LOWER(p.name)"; // se convierte en minúscula
				String lowergrp = "LOWER(p.grp)"; // se convierte en minúscula
				String LowerSearch = searchIn.toLowerCase(); // se convierte en minúscula

				// Se verifica si viene dato en el campo search para efectuar la búsqueda
				switch (searchIn) {
				case "": // viene sin busqueda por el like
					QueryTotal = SentenciaBase;
					break;
				default: // viene con el parametro para buscar por el like
					QueryTotal = SentenciaBase + " WHERE " + lowergrp + " LIKE  " + "'%" + LowerSearch + "%'" + " OR "
							+ lowername + " LIKE " + "'%" + LowerSearch + "%'";
				}

				// Busca si es ascendente o descendente
				switch (orderIn) {
				case 1: // ordena por group, ord y name ascendente
					QueryTotal = QueryTotal + " ORDER BY " + grp + " ASC" + "," + ord + " ASC" + "," + name + " ASC";
					break;

				case 2:// ordena por group ascendente
					QueryTotal = QueryTotal + " ORDER BY " + grp + " ASC";
					break;

				case 3: // ordena por ord ascendente
					QueryTotal = QueryTotal + " ORDER BY " + ord + " ASC";
					break;

				case 4: // ordena por name ascendente
					QueryTotal = QueryTotal + " ORDER BY " + name + " ASC";
					break;

				case -1:// ordena por group, ord y name descendente
					QueryTotal = QueryTotal + " ORDER BY " + grp + " DESC" + "," + ord + " DESC" + "," + name + " DESC";
					break;

				case -2:// ordena por group descendente
					QueryTotal = QueryTotal + " ORDER BY " + grp + " DESC";
					break;

				case -3:// ordena por ord descendente
					QueryTotal = QueryTotal + " ORDER BY " + ord + " DESC";
					break;

				case -4:// ordena por name descendente
					QueryTotal = QueryTotal + " ORDER BY " + name + " DESC";
					break;

				default:
					respuesta.setMsg("Error interno del servidor");
					estatus = HttpStatus.INTERNAL_SERVER_ERROR;
					return new ResponseEntity(respuesta, estatus);
				}

				// Se mapea la entidad se le pasa el query y lo bota como un tipo de objeto
				// ParamDto-Buscar en los DTO
				TypedQuery<PrivilegeUniqueResponseDto> privile = entityManager.createQuery(QueryTotal,
						PrivilegeUniqueResponseDto.class);
				cuantosregistro = (long) privile.getResultList().size();
				privile.setFirstResult(offsetIn);
				privile.setMaxResults(numofrecordsIn);

				List<PrivilegeUniqueResponseDto> privilegs = privile.getResultList();

				// 4. Recorrer la lista y procesar los resultados
				for (PrivilegeUniqueResponseDto priv : privilegs) {
					privilegios.setId(priv.getId());
					privilegios.setGroup(priv.getGrp());
					privilegios.setCrud(priv.getCrud());
					privilegios.setName(priv.getName());
					
					query = entityManager.createQuery(SentenciaPrivilegios, RolesAssignedResponseDto.class);

					query.setParameter("privilegioId", priv.getId()); // Correcto
					resultados = query.getResultList();
					for (RolesAssignedResponseDto resultado : resultados) {
						rolesAsignned.setId(resultado.getId());
						rolesAsignned.setName(resultado.getName());
						rolesAsignned.setAssigned(resultado.isAssigned());
						ro.add(rolesAsignned);
						rolesAsignned = new RolesAssignedResponseDto();
					}
					privilegios.setRoles(ro);
					ro = new ArrayList<>();
					records.add(privilegios);
					privilegios = new PrivileResponseDto();
				}
				
				privilegesResponseDto.setNumofrecords(cuantosregistro);
				privilegesResponseDto.setRecords(records);
				privilegesResponseDto.setSessionvalidthru(fechaComoCadena);

				return new ResponseEntity(privilegesResponseDto, HttpStatus.OK);
			} catch (Exception e) {
				respuesta.setMsg("Error interno del servidor ");
				estatus = HttpStatus.INTERNAL_SERVER_ERROR;
			}finally {
		        if (entityManager != null && entityManager.isOpen()) {
		            entityManager.close();
		        }
			}
		}
		return new ResponseEntity(respuesta, HttpStatus.OK);
	}

	@PostMapping("/privileges/{privilegeid}")
	public ResponseEntity<?> asignaDesasignaPrivilegios(HttpServletRequest request,
			@RequestBody RolAssinedRequestDto rolasigdes, @PathVariable("privilegeid") final Integer privilegeid)
			throws ParseException {
		RespuestaMsgDto respuesta = new RespuestaMsgDto("");
		HttpStatus estatus = HttpStatus.FORBIDDEN;
		RolesResponseDto rolesResponseDto = new RolesResponseDto();
		RoleDto detalleRoles;
		PrivilegesAllDto detallePrivilege;
		long cuantosregistro = 0;
		List<RoleDto> listasRoles = new ArrayList<>();
		List<PrivilegesAllDto> listasPrivelege = new ArrayList<>();
		String privilegeidChar = privilegeid.toString();
		String sessionid = request.getHeader("Authorization");
		Date fecha = new Date();
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String dataFormattata = formatter.format(fecha);
		Date fechaDate = formatter.parse(dataFormattata);
		AuditRequestDto auditDto = new AuditRequestDto();

		String searchIn = "";

		int orderIn = 0;
		int offsetIn = 0;
		int numofrecordsIn = 0;

		Optional<Params> deSessDuration = paramsRepository.findByParamname("SESSION_DURATION");
		String SessionDuration = deSessDuration.get().getValue();
		int duracionSession = Integer.parseInt(SessionDuration);
		Date fecha2 = new Date();
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(fecha2); // tuFechaBase es un Date;
		// calendar.add(Calendar.MINUTE, minutosASumar); //minutosASumar es int.
		calendar.add(Calendar.MINUTE, duracionSession); // horasASumar es int.
		// lo que más quieras sumar
		Date ValidThrufechaSalida = calendar.getTime(); // Y ya tienes la fecha sumada.
		SimpleDateFormat salida = new SimpleDateFormat("yyyy-MM-dd HH:mm"); // 2024-12-23 23:00
		String fechaComoCadena = salida.format(ValidThrufechaSalida);
		System.out.println(fechaComoCadena);
		RolPrivilegioAssignedResponseDto rolPrivilegioAssignedResponseDto = new RolPrivilegioAssignedResponseDto();
		Optional<Roles> roles;
		int cuentaExiste = 0;
		String module = "";
		String Descmodule = "";

		Rolesprivileges rolesPrivileges = new Rolesprivileges();
		try {
			if (sessionid == null) {
				respuesta.setMsg("Llamada al servicio malformado");
				estatus = HttpStatus.BAD_REQUEST;
				return new ResponseEntity(respuesta, estatus);
			} else {
				sessionid = sessionid.substring(7);
				// verifico si la sesión del usuario existe.
				Optional<Users> encontreSessionUsuario = usersRepository.getBySessionid(sessionid);
				if (encontreSessionUsuario.isPresent()) { // si la sesión del usuario existe
					
					
					  Date FechaReg = encontreSessionUsuario.get().getValidthru(); 
					   //Llamada a la funcion que validad el tiempo de Session, retorna la fecha sumandole el tiempo de session activa, y vacio si no esta activa
					   
					   // fechaComoCadena  = securityService.consultarSessionActiva(FechaReg,fecha2);
					    fechaComoCadena  = securityService.consultarSessionActiva(FechaReg,fecha2,encontreSessionUsuario.get().getId());
					   
					    if (fechaComoCadena=="") {
						   
						   
					    	RespuestaMsgDto respuestaDto2;
						       String var2 = "";
							boolean bloked2 = false;
							respuestaDto2 = new RespuestaMsgDto(var2);
							//respuestaDto.setBlocked(bloked);
							respuestaDto2.setMsg("Sesión expirada o invalida"); 
							return new ResponseEntity(respuestaDto2, HttpStatus.UNAUTHORIZED);
						   
					    }
					    
					    
					// Obtengo el rol de usuario y verifico si corresponde al privilegio del usuario
					int rolisvalid = 0;
					rolisvalid = auditRepository.getCantbyRolAndPrivi(encontreSessionUsuario.get().getRolid().getId(),
							621);
					if (rolisvalid == 0) {
						respuesta.setMsg("No tiene los Privilegios");
						return new ResponseEntity(respuesta, HttpStatus.FORBIDDEN);
					}

					// Procedo asignar o desasignar el privilegio al rol
					boolean existeRol = rolesRepository.existsById(rolasigdes.getRoleid());
					if (!existeRol) {
						respuesta.setMsg("Registro no encontrado");
						return new ResponseEntity(respuesta, HttpStatus.NOT_FOUND);
					} else {
						// /Busco si el rol y el privilegio existen. Si existe lo borro sino lo asigno
						cuentaExiste = rolesPrivilegesRepository.existeRolPrivilegio(rolasigdes.getRoleid(),
								privilegeid);
						if (cuentaExiste == 0) {// asigno
							rolesPrivileges.setPrivilegeid(privilegeid);
							rolesPrivileges.setRolid(rolasigdes.getRoleid());
							rolesPrivileges.setCreatedat(fecha2);
							rolesPrivileges.setModifiedat(fecha2);
							rolesPrivilegesRepository.save(rolesPrivileges);
							rolPrivilegioAssignedResponseDto.setSessionvalidthru(fechaComoCadena);
							rolPrivilegioAssignedResponseDto.setRoleid(rolasigdes.getRoleid());
							rolPrivilegioAssignedResponseDto.setPrivilegeid(privilegeid);
							rolPrivilegioAssignedResponseDto.setAssigned(true);
							Optional<Roles> roleamodificar = rolesRepository.findById(rolasigdes.getRoleid());

							////////////////////////// INSERCION BLOQUE DE AUDITORIA///////////////////
							module = "Privilegios";
							Descmodule = "Se asignaron los privilegios del rol: " + roleamodificar.get().getName() +" con el id del Privilegio: "+privilegeid;
							//Descmodule = "Asigno Rol del Sistema con id: " + rolasigdes.getRoleid()
								//	+ " Privilegio con id: " + privilegeid;

							auditDto.setIpaddr(HttpReqRespUtils.getClientIpAddressIfServletRequestExist());

							String singo1 = "(";
							String singo2 = ")";
							String usryemail = encontreSessionUsuario.get().getUsr().concat(" ").concat(singo1)
									.concat(encontreSessionUsuario.get().getEmail().concat(singo2));
							auditDto.setUserref(usryemail);
							auditDto.setModule(module);
							auditDto.setDesc(Descmodule);
							auditDto.setCreatedat(fechaDate);
							usersService.registrarAuditSesion(auditDto);

							// return new ResponseEntity(rolPrivilegioAssignedResponseDto, HttpStatus.OK);
							URI location = URI.create("/privileges/".concat(privilegeidChar)); // O la URL correcta para
																								// tu recurso
							return ResponseEntity.created(location).body(rolPrivilegioAssignedResponseDto);
						} else {// desasigno
							rolesPrivilegesRepository.borrarRolPrivilegio(rolasigdes.getRoleid(), privilegeid);
							rolPrivilegioAssignedResponseDto.setSessionvalidthru(fechaComoCadena);
							rolPrivilegioAssignedResponseDto.setRoleid(rolasigdes.getRoleid());
							rolPrivilegioAssignedResponseDto.setPrivilegeid(privilegeid);
							rolPrivilegioAssignedResponseDto.setAssigned(false);
							////////////////////////// INSERCION BLOQUE DE AUDITORIA///////////////////
							Optional<Roles> roleamodificar = rolesRepository.findById(rolasigdes.getRoleid());
							module = "Privilegios";
							//Descmodule = "Desasignación de Rol del Sistema con id: " + rolasigdes.getRoleid()
								//	+ " Privilegio con id: " + privilegeid;
							Descmodule = "Se eliminaron los privilegios del rol: " + roleamodificar.get().getName()+" con el id del Privilegio: "+privilegeid;

							auditDto.setIpaddr(HttpReqRespUtils.getClientIpAddressIfServletRequestExist());

							String singo1 = "(";
							String singo2 = ")";
							String usryemail = encontreSessionUsuario.get().getUsr().concat(" ").concat(singo1)
									.concat(encontreSessionUsuario.get().getEmail().concat(singo2));
							auditDto.setUserref(usryemail);
							auditDto.setModule(module);
							auditDto.setDesc(Descmodule);
							auditDto.setCreatedat(fechaDate);
							usersService.registrarAuditSesion(auditDto);
							// return new ResponseEntity(rolPrivilegioAssignedResponseDto, HttpStatus.OK);

							URI location = URI.create("/privileges/".concat(privilegeidChar)); // O la URL correcta para
																								// tu recurso
							return ResponseEntity.created(location).body(rolPrivilegioAssignedResponseDto);
							// return ResponseEntity.created(location).body(privilegesResponseDto);
						}

					}

				}
			}
		} catch (Exception e) {
			respuesta.setMsg("Error interno del servidor");
			estatus = HttpStatus.INTERNAL_SERVER_ERROR;
		} 
		return new ResponseEntity(respuesta, HttpStatus.OK);
	}
}
