package es.uv.saic.web;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
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 org.springframework.web.multipart.MultipartFile;

import es.uv.saic.domain.EvidenciaIndicadorEnquesta;
import es.uv.saic.domain.InstanciaTascaVer;
import es.uv.saic.domain.Plantilla;
import es.uv.saic.domain.Proces;
import es.uv.saic.domain.Tipus;
import es.uv.saic.domain.Usuari;
import es.uv.saic.dto.RolDTO;
import es.uv.saic.feign.PlantillaClient;
import es.uv.saic.feign.ProceduresClient;
import es.uv.saic.feign.UsuariClient;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;


@Controller
public class ProceduresController {

	@Autowired
	private ProceduresClient pc;

	@Autowired
	private PlantillaClient plc;

	@Autowired 
	private UsuariClient uc;

	@Value("${saic.data.filePath}")
	private String filePath;
		
	/* Redirect root to /procedures */
	@GetMapping("/")
	public void getRoot(Model model, Authentication auth, HttpSession session, HttpServletResponse response) throws IOException {
		response.sendRedirect("/procedures");
	}
	
	/*
	 * Load the list of active procedure instances for the logged-in user
	 * 
	 * @param model 
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param _new Optional parameter to indicate a new access
	 * @return The name of the view to render
	 */
	@GetMapping("/procedures")
	public String getActiveInstances(Model model, Authentication auth, HttpSession session, @RequestParam(required = false) String _new) {
		
		HashMap<String, Object> response =
                pc.getActiveInstances( _new, ((Usuari) auth.getPrincipal()).getUsuari());

        if (response == null) {
			return "401";
        }

		model.addAllAttributes(response);

		return "procedures";
	}
	
	/*
	 * Loads a procedure instance and its tasks
	 * 
	 * @param model 
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param id Instancia ID Instance to load
	 * @return The name of the view to render
	 */
	@GetMapping("/procedure/{id}")
	public String getInstance(Model model, Authentication auth, HttpSession session, @PathVariable BigInteger id) {
		
		HashMap<String, Object> response =
                pc.getInstance(id, ((Usuari) auth.getPrincipal()).getUsuari());
		
		if(session.getAttribute("location") != null) {
			model.addAttribute("location", session.getAttribute("location"));
		}
		else {
			session.setAttribute("location", "procedures");
			model.addAttribute("location", "procedures");
		}		

		if(response == null){
			return "401";
		}
		
        model.addAllAttributes(response);
		return "procedure";
	}
	

	/*
	 * Updates a task instance with evidence files (for specific task types)
	 * 
	 * @param model
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param id Instancia ID Instance to load
	 * @param params Form parameters
	 * @param evidencias List of evidence files (if any)
	 * @return The number of files uploaded (as a String)
	 * @throws IllegalStateException
	 * @throws IOException
	 * @throws InterruptedException
	 */
	@PostMapping("/procedure/files/{id}")
	@ResponseBody 
	public String updateInstanciaTascaEvidencia(Model model, Authentication auth, HttpSession session, @PathVariable BigInteger id, @RequestParam Map<String,String> params, 
		@RequestParam(required = true) List<MultipartFile> evidencias) throws IllegalStateException, IOException {
			
		List<File> files;
		
		// Convert MultipartFiles (evidencias) to Files before sending to the ProceduresClient
		files = new ArrayList<>();
		for (MultipartFile multipartFile : evidencias) {
			File convFile = new File(filePath + multipartFile.getOriginalFilename());
			multipartFile.transferTo(convFile);
			files.add(convFile);
		}

		HashMap<String, Object> response =
                pc.updateInstanciaTascaEvidencia(id, params, files, ((Usuari) auth.getPrincipal()).getUsuari());
        
		if (response != null && response.get("ammount") != null) {
			model.addAllAttributes(response);
			return response.get("ammount").toString();
        }
	
		return "0";
	}
	
	/*
	 * Updates a task instance
	 * 
	 * @param model 
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param id Instancia ID Instance to load
	 * @param params Form parameters
	 * @param evidencias List of evidence files (if any)
	 * @return The name of the view to render
	 */
	@PostMapping("/procedure/{id}")
	public String updateInstanciaTasca(Model model, Authentication auth, HttpSession session, @PathVariable BigInteger id, @RequestParam Map<String,String> params, 
			@RequestParam(required = false) List<MultipartFile> evidencias) throws IllegalStateException, IOException, InterruptedException {
		model.addAttribute("location", "procedures");

		HashMap<String, Object> response =
                pc.updateInstanciaTasca(id, params, evidencias, ((Usuari) auth.getPrincipal()).getUsuari());
        
		if (response == null) {
			return "401";
		}

		model.addAllAttributes(response);
		return "procedure";
	}
	

	/*
	 * Saves a draft of a task instance (for specific task types)
	 * 
	 * @param model
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param id Instancia ID Instance to load
	 * @param text Text content of the draft
	 * @param manual Whether the save was manually triggered by the user
	 * @return The timestamp of the save operation formatted as a String
	 */
	@PostMapping("/procedure/save/{id}")
	@ResponseBody
	public String saveDraft(Model model, Authentication auth, HttpSession session, @PathVariable BigInteger id, @RequestParam String text, 
							@RequestParam boolean manual) {
		HashMap<String, Object> response =
                pc.saveDraft(id, text, manual, ((Usuari) auth.getPrincipal()).getUsuari());
		
		if(session.getAttribute("location") != null) {
			model.addAttribute("location", session.getAttribute("location"));
		}
		else {
			session.setAttribute("location", "procedures");
			model.addAttribute("location", "procedures");
		}
		
		return response.get("date") == null ? "No se pudo obtener la fecha" : response.get("date").toString();
	}

	/*
	 * Get all drafts for a given task instance
	 * @param model
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param id InstanciaTasca ID to load drafts for
	 * @return The name of the view to render
	 */
	@GetMapping("/procedure/drafts/{id}")
	public String getDrafts(Model model, Authentication auth, HttpSession session, @PathVariable BigInteger id) {
		HashMap<String, Object> response =
                pc.getDrafts(id);
		
		if (response == null) {
			return "401";	
        }

		model.addAllAttributes(response);
		return "procedure_versions";
	}

	/*
	 * Get a specific draft for a given task instance
	 * @param model
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param id InstanciaTasca ID to load drafts for
	 * @param dataMod Timestamp of the draft to load
	 * @return The InstanciaTascaVer object representing the draft
	 */
	@GetMapping("/procedure/draft/{id}")
	@ResponseBody
	public InstanciaTascaVer getDraft(Model model, Authentication auth, HttpSession session, @PathVariable BigInteger id, @RequestParam Timestamp dataMod) {
		return pc.getDraft(id, dataMod);
	}

	/*
	 * Restore a specific draft for a given task instance
	 * @param model
	 * @param auth Authentication
	 * @param session HttpSession
	 * @param id InstanciaTasca ID to restore draft for
	 * @param dataMod Timestamp of the draft to restore
	 * @return "1" if successful, "0" otherwise
	 */
	@PostMapping("/procedure/draft/{id}")
	@ResponseBody
	public String restoreDraft(Model model, Authentication auth, HttpSession session, @PathVariable BigInteger id, @RequestParam Timestamp dataMod) {
		return pc.restoreDraft(id, dataMod);
	}

    // POST que se utiliza para conseguir los cursos a partir de la titulación
	@PostMapping("/search/years")
	public String getYearsByCenterTitulation(Model model, Authentication auth, 
			@RequestParam(name="centers[]", required=false) List<Integer> centres,
			@RequestParam("titulations[]") List<Integer> titulacions) throws IOException {		
			
		HashMap<String, Object> response =
			pc.getYearsByCenterTitulation(centres, titulacions, ((Usuari) auth.getPrincipal()).getUsuari());
		
		if (response == null) {
			return "401";	
        }

		model.addAllAttributes(response);
		return "components/selector_cursos";
	}

    // POST que se utiliza para conseguir los procedimiento que se han llevado a cabo por cursos y por titulación
	@PostMapping("/search/procedures")
	public String getProceduresByCenterTitulationYear(Model model, Authentication auth,
			@RequestParam(name="centers[]", required=false) List<Integer> centres,
			@RequestParam("years[]") List<Integer> cursos,
			@RequestParam("titulations[]") List<Integer> titulacions) throws IOException {	
		HashMap<String, Object> response =
                pc.getProceduresByCenterTitulationYear(centres, cursos, titulacions, ((Usuari) auth.getPrincipal()).getUsuari());
        
		if (response == null) {
			return "401";	
        }

		model.addAllAttributes(response);
		return "components/selector_processos";
	}

    @PostMapping("/find/procedure")
	public String findProcedure(Model model, Authentication auth, @RequestParam("procedure") Integer idProces, 
			@RequestParam("action") String action) throws IOException {	
		
		HashMap<String, Object> response =
                pc.findProcedure(idProces.toString(), action);
		if (response != null) {
			model.addAllAttributes(response);

			if (action.equals("remove")) {
				return "components/form_procedure_remove_confirm";
			} else if (action.equals("duplicate") || action.equals("edit") || idProces == 0) {
				return "components/form_procedure";
			}
		}

		return "401";
	}

	// POST para crear el form de creación de plantilla
	@PostMapping("/template/form")
	public String formTemplate(Model model, Authentication auth, @RequestParam("id") Integer idPlantilla,
			@RequestParam("action") String action) throws IOException {	
		
				HashMap<String, Object> response =
                pc.formTemplate(idPlantilla, action);
            if (response != null && response.get("redirect") != null) {
                model.addAllAttributes(response);
                return (boolean) response.get("redirect") ? "components/form_template" : "";
            }

		return "401";
	}


	// GET para renderizar el formulario de cración de nueva tarea
	@GetMapping("/newTask/{i}")
	public String newTaskForm(Model model, Authentication auth, @PathVariable Integer i) throws IOException {	
		List<RolDTO> roles = uc.findAssignables();
		List<Tipus> tipus = pc.findAll();
		List<Plantilla> templates = plc.findAll();
		model.addAttribute("tipus", tipus);
		model.addAttribute("roles", roles);
		model.addAttribute("i", i);
		model.addAttribute("templates", templates);
		return "components/form_procedure_task";
	}

	@ResponseBody
	@PostMapping("/find/template")
	public String findTemplate(Model model, Authentication auth, @RequestParam("id") Integer idPlantilla) throws IOException {	
		Plantilla p = plc.findByID(idPlantilla);
		return p.getText();
	}
	
	// GET para comprobar si una plantilla esta siendo usada
	@ResponseBody
	@GetMapping("/template/used/{idPlantilla}")
	public int isTemplateUsed(Model model, Authentication auth, @PathVariable("idPlantilla") Integer idPlantilla) throws IOException {	
		Boolean u = plc.isUsed(idPlantilla);
		return u ? 1 : 0;
	}

	@ResponseBody
	@PostMapping("/template/edit")
	public String templateEdit(Model model, Authentication auth, @RequestParam("id") Integer idPlantilla,@RequestParam("text") String text, @RequestParam("versio") Integer versio,
			@RequestParam("nomCas") String nomCas, @RequestParam("nomVal") String nomVal, @RequestParam("ambit") String ambit) throws IOException {
		Plantilla p = plc.findByID(idPlantilla);
		p.setNomCas(nomCas);
		p.setNomVal(nomVal);
		p.setText(text);
		p.setAmbit(ambit);
		plc.save(p);
		return "1";
	}

	@ResponseBody
	@PostMapping("/template/save")
	public String templateSave(Model model, Authentication auth, @RequestParam("text") String text, @RequestParam("codi") String codi, @RequestParam("versio") Integer versio,
			@RequestParam("nomCas") String nomCas, @RequestParam("nomVal") String nomVal, @RequestParam("ambit") String ambit) throws IOException {	
		Plantilla p = new Plantilla();
		Plantilla p2 = plc.findByVersioCodiAmbit(versio, codi, ambit);
		if(p2 != null) {
			return "0";
		}
		p.setCodi(codi);
		p.setVersio(versio);
		p.setNomCas(nomCas);
		p.setNomVal(nomVal);
		p.setText(text);
		p.setAmbit(ambit);
		plc.save(p);
		return "1";
	}
	
	@ResponseBody
	@PostMapping("/draft/save/{id}")
	public String saveTemplate(Model model, Authentication auth, @PathVariable("id") Integer idPlantilla, 
			@RequestParam("text") String text) throws IOException {	
		Plantilla p = plc.findByID(idPlantilla);
		p.setText(text);
		plc.save(p);
		return "1";
	}

	@DeleteMapping("/template/form")
	@ResponseBody
	public String formTemplateRemove(Model model, Authentication auth, @RequestParam("id") Integer idPlantilla) throws IOException {	
		if(plc.isUsed(idPlantilla)){
			return "0";
		}
		else {
			Plantilla p = plc.findByID(idPlantilla);
			plc.delete(p);
			return "1";
		}
	
	}

	@PostMapping("/find/template/inds")
	public String findTemplatesInds(Model model, Authentication auth, @RequestParam("procedure") Integer idProces, 
			@RequestParam("center") String idCentre, @RequestParam("titulation") String idTitulacio,
			@RequestParam("ev") String evidencia) throws IOException {	
		
		Proces p = this.pc.findProcesByID(idProces);
		List<EvidenciaIndicadorEnquesta> inds = this.pc.getByProcesEvidencia(p.getNomProces(), evidencia);
		model.addAttribute("inds", inds);
		model.addAttribute("proces", p.getNomProces());
		model.addAttribute("idCentre", idCentre);
		model.addAttribute("idTitulacio", idTitulacio);
		model.addAttribute("evidencia", evidencia);
		
		return "components/form_templates_indicators";
	}
}