package es.uv.saic.web;

import java.io.IOException;
import java.math.BigInteger;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import es.uv.saic.domain.Acreditacio;
import es.uv.saic.domain.AcreditacioTransfer;
import es.uv.saic.domain.DummyDataTransfer;
import es.uv.saic.domain.Plantilla;
import es.uv.saic.domain.Usuari;
import es.uv.saic.dto.OrganDTO;
import es.uv.saic.dto.ProcesDTO;
import es.uv.saic.dto.RolDTO;
import es.uv.saic.dto.TemplateDataDTO;
import es.uv.saic.feign.AdminClient;
import es.uv.saic.feign.IndicadorClient;
import es.uv.saic.feign.OrganClient;
import es.uv.saic.feign.PlantillaClient;
import es.uv.saic.feign.ProceduresClient;
import es.uv.saic.feign.UsuariClient;

@Controller
public class AdminController {

	@Autowired
	private OrganClient oc;
	@Autowired
	private ProceduresClient pc;
	@Autowired
	private AdminClient ac;
	@Autowired
	private UsuariClient uc;
	@Autowired
	private IndicadorClient ic;
	@Autowired
	private PlantillaClient plc;
	
	public static final String DDMMYYYY = "dd/MM/yyyy";
	
	// GET para conseguir todas las instancias del sistema en ese momento.
	@GetMapping("/admin/instances")
	@Secured({"ROLE_ADMIN"})
	public String getInstances(Model model, Authentication auth) {		
		this.loadInstanceData(model);
		return "adminInstances";
	}

	public void loadInstanceData(Model model) {
		List<DummyDataTransfer> centres = new ArrayList<DummyDataTransfer>();
		List<DummyDataTransfer> titulacions = new ArrayList<DummyDataTransfer>();
		List<DummyDataTransfer> procediments = new ArrayList<DummyDataTransfer>();
		
		for(OrganDTO o : oc.getCentres()) {
			DummyDataTransfer a = new DummyDataTransfer();
			a.setText(o.getNomCas());
			a.setText2(o.getNomVal());
			a.setValue(o.getLugar());
			centres.add(a);
		}

		for(ProcesDTO p : pc.getAll()) {
			DummyDataTransfer a = new DummyDataTransfer();
			a.setText(p.getNomProces()); 
			a.setText2("(" + p.getIdProces() + " - Ver. "+ p.getVersio() +") " + p.getNomProces() + " " + p.getTitolCas());
			a.setText3("(" + p.getIdProces() + " - Ver. "+ p.getVersio() +") " + p.getNomProces() + " " + p.getTitolVal());
			a.setValue(p.getIdProces());
			procediments.add(a);
		}
		model.addAttribute("centers", centres);
		model.addAttribute("titulations", titulacions);
		model.addAttribute("procedures", procediments);
	}
	
	// POST que recoge los campos introducidos en los inputs (Proceso, centro y titulación) y busca las instancias relacionadas
	@PostMapping("/admin/instances")
	@Secured({"ROLE_ADMIN"})
	public String instantiate(Model model, Authentication auth, 
			@RequestParam("procedure") Integer idProces,
			@RequestParam("center") Integer idCentre,
			@RequestParam("titulation") Integer idTitulacio,
			HttpSession session) {
		
		HashMap<String, Object> map = ac.instantiate(idProces, idCentre, idTitulacio);

		model.addAllAttributes(map);

		this.loadInstanceData(model);
		
		return "adminInstances";
	}

	// GET que recoge todos los procedimientos del sistema
	@GetMapping("/admin/procedures")
	@Secured({"ROLE_ADMIN"})
	public String getProcedures(Model model, Authentication auth) {
		this.loadProceduresData(model);
		return "adminProcedures";
	}
	
	// Función utilizada en el GET que recoge todos los procedimientos
	public void loadProceduresData(Model model) {
		List<DummyDataTransfer> procediments = new ArrayList<DummyDataTransfer>();
		for(ProcesDTO p : pc.getAll()) {
			DummyDataTransfer a = new DummyDataTransfer();
			a.setText(p.getNomProces());
			a.setText2("(" + p.getIdProces() + " - Ver. "+ p.getVersio() +") " + p.getNomProces() + " " + p.getTitolCas());
			a.setText3("(" + p.getIdProces() + " - Ver. "+ p.getVersio() +") " + p.getNomProces() + " " + p.getTitolVal());
			a.setValue(p.getIdProces());
			procediments.add(a);
		}
		model.addAttribute("procedures", procediments);
	}
	
	// POST que elimina una instancia concreta
	@PostMapping("/admin/instance/delete")
	@Secured({"ROLE_ADMIN"})
	public void deleteInstance(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstancia) throws IOException {
		ac.deleteInstance(idInstancia);
		
		response.sendRedirect("/supervision");
	}
	
	// POST que reinicia una instancia por completo
	@PostMapping("/admin/instance/clear")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void clearInstance(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstancia) throws IOException {
		String instanciaid = ac.clearInstance(idInstancia);
		
		response.sendRedirect("/procedure/"+instanciaid);
	}
	
	// POST que establece que una instancia ha sido cerrada, cambiandole su estado
	@PostMapping("/admin/instance/close")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void closeInstance(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstancia) throws IOException {
		String instanciaid = ac.closeInstance(idInstancia);
		
		response.sendRedirect("/procedure/"+instanciaid);
	}
	
	// POST que se encarga de eliminar una tarea concreta de una instancia
	@PostMapping("/admin/instance/task/remove")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void removeTask(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstanciaTasca) throws IOException {
		String instanciaid = ac.removeTask(idInstanciaTasca);

		response.sendRedirect("/procedure/"+instanciaid);
	}
	
	// POST que se encarga de reiniciar por completo una tarea de una instancia
	@PostMapping("/admin/instance/task/clear")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void clearTask(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstanciaTasca) throws IOException {
		String instanciaid = ac.clearTask(idInstanciaTasca);
		
		response.sendRedirect("/procedure/"+instanciaid);
	}
	
	// POST que reinicia una tarea del procedimiento, eliminando datos anteriores y creando una nueva vesión de esta
	@PostMapping("/admin/instance/task/reload")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void reloadTask(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstanciaTasca) throws IOException {
		String instanciaid = ac.reloadTask(idInstanciaTasca);
		
		response.sendRedirect("/procedure/"+instanciaid);
	}
	
	// POST que tiene como objetivo reactivar una tarea
	@PostMapping("/admin/instance/task/reactivate")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void activateTask(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstanciaTasca) throws IOException {
		String instanciaid = ac.activateTask(idInstanciaTasca);
		
		response.sendRedirect("/procedure/"+instanciaid);
	}
	
	// POST que edita la información de una tarea ya existente
	@PostMapping("/admin/instance/task/edit")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void editTask(Model model, Authentication auth, HttpServletResponse response, @RequestParam BigInteger idInstanciaTasca) throws IOException {
		String instanciaid = ac.editTask(idInstanciaTasca);
		
		response.sendRedirect("/procedure/"+instanciaid);
	}
	
	@GetMapping("/admin/mailing")
	@Secured({"ROLE_ADMIN"})
	public String getMailing(Model model, Authentication auth, HttpServletResponse response) {
		List<RolDTO> rols = uc.findAllRols();
		List<OrganDTO> centres = oc.getActiveCentres();
		model.addAttribute("centres", centres);
		model.addAttribute("rols", rols);
		
		return "mailing";
	}
	
	@PostMapping("/admin/mailing")
	@Secured({"ROLE_ADMIN"})
	public String sendMails(Model model, Authentication auth, HttpServletResponse response, 
			@RequestParam Integer idRol, @RequestParam("centres[]") List<Integer> centres,
			@RequestParam String subject, @RequestParam String body) {
		ac.sendMails(idRol, centres, subject, body);

		List<RolDTO> rols = uc.findAllRols();
		List<OrganDTO> centresa = oc.getActiveCentres();
		model.addAttribute("centres", centresa);
		model.addAttribute("rols", rols);
		model.addAttribute("sent", true);
		
		return "mailing";
	}
	
	// POST para la creación de un procedimiento nuevo
	@PostMapping("/admin/procedures/new")
	@Secured({"ROLE_ADMIN"})
	public String newProcedure(Model model, Authentication auth, HttpServletResponse response, @RequestParam Map<String,String> params, 
			@RequestParam(name="idTascap[]", required=false) List<String> idTascap, 
			@RequestParam(name="dataLimit[]", required=false) List<String> dataLimit, 
			@RequestParam(name="tipus[]", required=false) List<String> tipus,
			@RequestParam(name="codiEvidencia[]", required=false) List<String> codiEvidencia, 
			@RequestParam(name="idTascaSeg[]", required=false) List<String> idTascaSeg, 
			@RequestParam(name="idTascaSeg2[]", required=false) List<String> idTascaSeg2,
			@RequestParam(name="opcions[]", required=false) List<String> opcions,
			@RequestParam(name="idRol[]", required=false) List<String> idRol, 
			@RequestParam(name="titolCas[]", required=false) List<String> titolCas, 
			@RequestParam(name="titolVal[]", required=false) List<String> titolVal,
			@RequestParam(name="descripcioCas[]", required=false) List<String> descripcioCas, 
			@RequestParam(name="descripcioVal[]", required=false) List<String> descripcioVal,
			@RequestParam(name="nomEvidenciaCas[]", required=false) List<String> nomEvidenciaCas, 
			@RequestParam(name="nomEvidenciaVal[]", required=false) List<String> nomEvidenciaVal,
			@RequestParam(name="idPlantilla[]", required=false) List<String> idPlantilla,
			@RequestParam(name="informe[]", required=false) List<Boolean> informe) throws NumberFormatException, ParseException {
	
		ac.newProcedure(params, idTascap, dataLimit, tipus, codiEvidencia, idTascaSeg, idTascaSeg2, opcions, idRol, titolCas, titolVal, descripcioCas, descripcioVal, nomEvidenciaCas, nomEvidenciaVal, idPlantilla, informe);
		
		this.loadProceduresData(model);
		
		return "adminProcedures";
	}
	
	// POST para editar un procedimiento ya existente
	@PostMapping("/admin/procedures/edit")
	@Secured({"ROLE_ADMIN"})
	public String editProcedure(Model model, Authentication auth, HttpServletResponse response, @RequestParam Map<String,String> params, 
			@RequestParam(name="idTascap[]", required=false) List<String> idTascap, 
			@RequestParam(name="dataLimit[]", required=false) List<String> dataLimit, 
			@RequestParam(name="tipus[]", required=false) List<String> tipus,
			@RequestParam(name="codiEvidencia[]", required=false) List<String> codiEvidencia, 
			@RequestParam(name="idTascaSeg[]", required=false) List<String> idTascaSeg, 
			@RequestParam(name="idTascaSeg2[]", required=false) List<String> idTascaSeg2,
			@RequestParam(name="opcions[]", required=false) List<String> opcions,
			@RequestParam(name="idRol[]", required=false) List<String> idRol, 
			@RequestParam(name="titolCas[]", required=false) List<String> titolCas, 
			@RequestParam(name="titolVal[]", required=false) List<String> titolVal,
			@RequestParam(name="descripcioCas[]", required=false) List<String> descripcioCas, 
			@RequestParam(name="descripcioVal[]", required=false) List<String> descripcioVal,
			@RequestParam(name="nomEvidenciaCas[]", required=false) List<String> nomEvidenciaCas, 
			@RequestParam(name="nomEvidenciaVal[]", required=false) List<String> nomEvidenciaVal,
			@RequestParam(name="idPlantilla[]", required=false) List<String> idPlantilla,
			@RequestParam(name="informe[]", required=false) List<Boolean> informe) 
					throws NumberFormatException, ParseException {

		ac.editProcedure(params, idTascap, dataLimit, tipus, codiEvidencia, idTascaSeg, idTascaSeg2, opcions, idRol, titolCas, titolVal, descripcioCas, descripcioVal, nomEvidenciaCas, nomEvidenciaVal, idPlantilla, informe);

		this.loadProceduresData(model);
		
		return "adminProcedures";
	}
	
	// POST que elimina un procedimiento
	@PostMapping("/admin/procedures/remove")
	@Secured({"ROLE_ADMIN"})
	public String removeProcedure(Model model, Authentication auth, HttpServletResponse response, @RequestParam("idProces") Integer idProces){
		ac.removeProcedure(idProces);
		
		this.loadProceduresData(model);
		
		return "adminProcedures";
	}
	
	// POST que le eliminar un usuario concreto del sitema
	@PostMapping("/admin/userrole/remove")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void removeUserrole(Model model, Authentication auth, HttpServletResponse response, HttpSession session, @RequestParam("idRol") Integer idRol, 
			@RequestParam("usuari") String usuari, @RequestParam("lugar") Integer lugar, @RequestParam("tlugar") String tlugar) throws IOException{
		
		HashMap<String, Object> map = ac.removeUserrole(idRol, usuari, lugar, tlugar);
				
		session.setAttribute("respCentres", (List<Integer>) map.get("respCentres"));
		session.setAttribute("respTitulacions", (List<Integer>) map.get("respTitulacions"));
		
		response.sendRedirect("/managers");
	}
	
	// POST para añadir un nuevo usuario al sistema
	@PostMapping("/admin/userrole/new")
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public void newUserrole(Model model, Authentication auth, HttpServletResponse response, HttpSession session, @RequestParam("idRol") Integer idRol, 
			@RequestParam(name="usuari", required=false) String usuari, @RequestParam("centre") Integer idCentre, 
			@RequestParam(name="titulacio", required=false) Integer idTitulacio, @RequestParam Map<String,String> params) throws IOException{

		HashMap<String, Object> map = ac.newUserrole(idRol, usuari, idCentre, idTitulacio, params);		

		session.setAttribute("respCentres", (List<Integer>) map.get("respCentres"));
		session.setAttribute("respTitulacions", (List<Integer>) map.get("respTitulacions"));
		session.setAttribute("roleExists", map.get("roleExists"));
		
		
		response.sendRedirect("/managers");
	}
	
	//¿POSIBLE ELIMINACIÓN?
	@GetMapping("/admin/templates2")
	@Secured({"ROLE_ADMIN", "ROLE_TESTER"})
	public String getTemplates(Model model, Authentication auth, HttpServletResponse response) {

		this.loadInstanceData(model);
		return "adminTemplates";
	}
	
	// GET que recoge todas las plantillas actuales del sistema
	@GetMapping("/admin/templates")
	@Secured({"ROLE_ADMIN", "ROLE_TESTER"})
	public String getTemplates2(Model model, Authentication auth, HttpServletResponse response) {
					
		List<String> evs = ac.getTemplates2();
		
		this.loadInstanceData(model);
		model.addAttribute("currentYear", LocalDate.now().getYear());
		model.addAttribute("evs", evs);
		return "adminTemplates2";
	}
	
	// POST que redefine que indicadores están asociados a una evidencia dentro de un proceso
	@PostMapping("/admin/templates/inds/update")
	@Secured({"ROLE_ADMIN"})
	public String updateTemplateInds(Model model, Authentication auth, HttpServletResponse response,
			@RequestParam(name="indicador[]", required=true) List<String> indicador,
			@RequestParam(name="enquesta[]", required=true) List<String> enquesta,
			@RequestParam(name="media[]", required=true) List<String> media,
			@RequestParam(name="evidencia", required=true) String evidencia,
			@RequestParam(name="proces", required=true) String proces
			) {
		
		ac.updateTemplateInds(indicador, enquesta, media, evidencia, proces);

		return "blank";
	}
	
	// GET para abrir el editor de plantillas
	@GetMapping("/admin/editor")  
	@Secured({"ROLE_ADMIN", "ROLE_TESTER"})
	public String getTemplateEditor(Model model, Authentication auth, HttpServletResponse response) {

		List<String> inds = this.ic.findTypes();
		List<Plantilla> templates = this.plc.findAll();
		model.addAttribute("templates", templates);
		model.addAttribute("inds", inds);
		this.loadInstanceData(model);
		if(((Usuari)auth.getPrincipal()).isDataTest()) {
			return "testerTemplateEditor";
		}
		return "adminTemplateEditor";
	}
	
	@GetMapping("/admin/editor/test")  
	@Secured({"ROLE_ADMIN", "ROLE_TESTER"})
	public String testTemplateEditor(Model model, Authentication auth, HttpServletResponse response,
			@RequestParam Integer idPlantilla, @RequestParam Integer idTitulacio, @RequestParam Integer idCentre, @RequestParam Integer curs) {
		
		Plantilla p = plc.findByID(idPlantilla);
		TemplateDataDTO td = new TemplateDataDTO(idTitulacio, idCentre, curs, p.getText());
		String text = plc.addTemplateData2(td);
		model.addAttribute("text", text);
		
		return "adminTemplateTest";
	}
	
	// GET para la carga del calendario
	@GetMapping("/admin/calendar")  
	@Secured({"ROLE_ADMIN"})
	public String calendar(Model model, Authentication auth, HttpServletResponse response) {

		this.loadInstanceData(model);
		return "adminCalendar";
	}
	
	@GetMapping("/admin/acredita/{curs}/{grup}/{tambit}")  
	@ResponseBody
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public List<AcreditacioTransfer> acreditacionsByCurs(Model model, Authentication auth, HttpServletResponse response,
			@PathVariable Integer curs, @PathVariable Integer grup, @PathVariable String tambit) {
		
		return ac.acreditacionsByCurs(curs, grup, tambit);
	}
	
	@PostMapping("/admin/acredita")  
	@ResponseBody
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public Acreditacio UpdateAcreditacio(Model model, Authentication auth, HttpServletResponse response, 
									@RequestParam String tlugar, @RequestParam Integer lugar, 
									@RequestParam Integer grupCurs, @RequestParam Integer grupNum, @RequestParam Integer cursImpla,
									@RequestParam String dataAcred, @RequestParam String dataRenov, @RequestParam String dataSegui,
									@RequestParam String dataVerif, @RequestParam Boolean recom, @RequestParam Boolean segui) throws ParseException {
		return ac.UpdateAcreditacio(tlugar, lugar, grupCurs, grupNum, cursImpla, dataAcred, dataRenov, dataSegui, dataVerif, recom, segui);
	}
	
}
