package es.uv.saic.domain;


import java.math.BigInteger;
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import es.uv.saic.dto.InstanciaGanttDTO;

@Repository
public interface InstanciaRepository extends JpaRepository<Instancia, BigInteger> {
	
	@Query(value=("SELECT id_instancia FROM instancies"), nativeQuery=true)
	List<BigInteger> findAllIds();
	
	@Query(value=("SELECT * FROM instancies WHERE tlugar = ?2 AND lugar = ?3 AND centre = ?4 AND titulacio = ?5 AND estat = 'F' "
			+ "AND id_proces IN(SELECT id_proces FROM processos WHERE nom_proces = ?1 AND curs_avaluat = ?6)"), nativeQuery=true)
	List<Instancia> findOlders(String proces, String tlugar, Integer lugar, Integer centre, Integer titulacio, Integer curs);
	
	@Query(value=("SELECT * FROM instancies WHERE id_instancia IN( "
			+ "SELECT ti.id_instancia FROM tasquesi ti "
			+ "LEFT JOIN tasques ta ON ti.id_tasca = ta.id_tasca "
			+ "LEFT JOIN instancies ie ON ie.id_instancia = ti.id_instancia "
			+ "LEFT JOIN processos pr ON pr.id_proces = ie.id_proces "
			+ "WHERE ie.tlugar = ?2 AND ie.lugar = ?3 AND ie.centre = ?4 AND ie.titulacio = ?5 AND ie.estat = 'F' "
			+ "      AND pr.nom_proces = ?1 AND pr.curs_avaluat = ?6 AND ta.id_tascap = ?7 AND ti.estat = ?8 "
			+ ");"), nativeQuery=true)
	List<Instancia> findOlders(String proces, String tlugar, Integer lugar, Integer centre, Integer titulacio, Integer curs, Integer idtascap, String estat);
	
	@Query(value=("SELECT * FROM instancies WHERE id_proces = ?1"), nativeQuery=true)
	List<Instancia> findByIdProces(Integer idProces);
	
	@Query(value=("SELECT * FROM instancies WHERE tlugar = ?1 AND lugar = ?2 AND centre = ?3 AND titulacio = ?4 "
			+ "AND id_proces IN(SELECT id_proces FROM processos WHERE nom_proces IN (?6) AND curs_avaluat = ?5)"), nativeQuery=true)
	List<Instancia> findByOrganCursNom(String tlugar, Integer lugar, Integer centre, Integer titulacio, Integer curs, List<String> nom);

	@Query(value=("""
			SELECT ia.* FROM instancies ia
			LEFT JOIN organs og ON ia.lugar = og.lugar AND ia.tlugar = og.tlugar
			LEFT JOIN processos pr ON ia.id_proces = pr.id_proces 
			WHERE og.lugar2 = ?1 AND og.tlugar2 = 'C' AND pr.curs_avaluat = ?2 AND pr.nom_proces IN(?3);
		"""), nativeQuery=true)
	List<Instancia> findByCentreCursNom(Integer lugar2, Integer curs, List<String> nom);
	
	@Query(value=("""
		SELECT ins.id_instancia AS id, pr.titol_cas AS titCas, pr.titol_val AS titVal, 
			MIN(LEAST(ins.data, ta.data_lim - 31)) AS start, MAX(GREATEST(ta.data_lim, tai.data_fet)) AS end, 100 AS progress, 
			CASE WHEN pr.sub_titol IS NULL THEN pr.nom_proces 
			  WHEN pr.sub_titol IS NOT NULL THEN CONCAT(pr.nom_proces, ' (', pr.sub_titol, ')') 
			END AS name, 
			CASE WHEN ins.estat = 'F' THEN 'gantt-task-done' 
			  WHEN ins.estat NOT IN('F', 'C') AND MAX(ta.data_lim) < current_date THEN 'gantt-task-pending' 
			  WHEN ins.estat NOT IN('F', 'C') AND MAX(ta.data_lim) >= current_date THEN 'gantt-task-delayed' 
			  WHEN ins.estat = 'C' THEN 'gantt-task-closed' 
			END AS custom_class 
		FROM instancies ins 
		LEFT JOIN processos pr ON pr.id_proces = ins.id_proces 
		LEFT JOIN tasques ta ON ins.id_proces = ta.id_proces 
		LEFT JOIN tasquesi tai ON ins.id_instancia = tai.id_instancia 
		WHERE ins.tlugar = ?1 AND ins.lugar = ?2 AND ins.centre = ?3 AND ins.titulacio IN(?4) 
			AND EXTRACT(YEAR FROM ins.data) >= ?5 AND EXTRACT(YEAR FROM ins.data) <= ?6 
		GROUP BY ins.id_instancia, pr.nom_proces, pr.sub_titol, pr.titol_cas, pr.titol_val, ins.data, ins.estat 
		ORDER BY 5;"""), nativeQuery=true)
	List<InstanciaGanttDTO> findByOrganBetweenCurs(String tlugar, Integer lugar, Integer centre, List<Integer> titulacio, Integer cursIni, Integer cursFi);
	
	@Query(value=("SELECT res.id_instancia FROM (SELECT DISTINCT ia.id_instancia, pr.curs_avaluat, ia.centre, ia.titulacio, pr.id_proces FROM instancies ia, processos pr " + 
			"WHERE ia.id_proces = pr.id_proces " + 
			"AND pr.nom_proces IN (?4) AND pr.curs_avaluat IN (?3) AND ia.titulacio IN (?2) AND ia.centre IN (?1) " + 
			"ORDER BY pr.curs_avaluat, ia.centre, ia.titulacio, pr.id_proces) AS res"), nativeQuery=true)
	List<BigInteger> filterSupervisables(List<Integer> centres, List<Integer> titulacions, List<Integer> cursos, List<String> procediments);
	
	@Query(value=("SELECT res.id_instancia FROM (SELECT DISTINCT ia.id_instancia, pr.curs_avaluat, ia.centre, ia.titulacio, pr.id_proces FROM instancies ia, processos pr " + 
			"WHERE ia.id_proces = pr.id_proces " + 
			"AND pr.nom_proces = ?4 AND pr.curs_avaluat = ?3 AND ia.titulacio = ?2 AND ia.centre = ?1 " + 
			"ORDER BY pr.curs_avaluat, ia.centre, ia.titulacio, pr.id_proces) AS res"), nativeQuery=true)
	BigInteger filterSupervisable(Integer centre, Integer titulacio, Integer curs, String procediment);
	
	@Query(value=("SELECT res.id_instancia FROM (SELECT DISTINCT ia.id_instancia, pr.curs_avaluat, ia.centre, ia.titulacio, pr.id_proces  " + 
			"FROM tasques ta, tasquesi ti, instancies ia, processos pr  " + 
			"WHERE ta.id_tasca = ti.id_tasca AND ti.id_instancia = ia.id_instancia AND ta.id_proces = pr.id_proces  " + 
			"AND ia.centre IN (?1) AND ia.titulacio IN (?2) AND pr.curs_avaluat IN (?3) AND ta.codi_evidencia IN (?4)  " +
			"ORDER BY pr.curs_avaluat, ia.centre, ia.titulacio, pr.id_proces) AS res"), nativeQuery=true)
	List<BigInteger> filterSupervisablesByEvidencies(List<Integer> centres, List<Integer> titulacions, List<Integer> cursos, List<String> evidencies);
	
    @Transactional
	@Query(value="WITH y AS( " + 
			"  INSERT INTO instancies " + 
			"  SELECT CAST((id_proces*1000 + lugar2) as bigint)*10000 + lugar, id_proces, tlugar, lugar, lugar2 centre, lugar titulacio, 'A' estat, NOW() AS data " + 
			"  FROM processos p, organs o  " + 
			"  WHERE id_proces=?1 AND ambit='T' AND lugar IN (?2) RETURNING id_instancia, id_proces " + 
			"), x AS( " + 
			"INSERT INTO tasquesi(id_tascai, id_instancia, id_tasca, version)  " + 
			"  SELECT (y.id_instancia*10000 + id_tascap) *100, y.id_instancia, id_tasca, 0 " + 
			"  FROM tasques t, y  " + 
			"  WHERE t.id_proces = y.id_proces " + 
			"  ORDER BY id_tascap " + 
			") " + 
			"SELECT id_instancia FROM y;", nativeQuery=true)
    BigInteger instantiateT(Integer idProces, Integer titulacio);
	
    @Transactional
	@Query(value="WITH y AS( " + 
			"  INSERT INTO instancies " + 
			"  SELECT CAST((id_proces*1000 + lugar) AS bigint)*10000 + 1, id_proces, tlugar, lugar, lugar  centre, ?3 titulacio, 'A' estat, NOW() AS data " + 
			"  FROM processos p, organs o " + 
			"  WHERE id_proces=?1 AND ambit='C' AND o.tlugar='C' AND lugar IN (?2) RETURNING id_instancia, id_proces " + 
			"), x AS( " + 
			"  INSERT INTO tasquesi(id_tascai, id_instancia, id_tasca, version) " + 
			"  (SELECT (y.id_instancia*10000 + id_tascap) *100, y.id_instancia, id_tasca, 0 " + 
			"  FROM tasques t, y " + 
			"  WHERE y.id_proces=t.id_proces " + 
			"  ORDER BY id_tascap) RETURNING id_tascai " + 
			") " + 
			"SELECT id_instancia FROM y;", nativeQuery=true)
	BigInteger instantiateC(Integer idProces, Integer centre, Integer titulacio);
    
    @Transactional
	@Query(value="WITH y AS( " + 
			"  INSERT INTO instancies " + 
			"  SELECT CAST((id_proces*1000) AS bigint)*10000 + 1, id_proces, 'C' AS tlugar, 0 lugar, 0 centre, ?2 titulacio, 'A' estat, NOW() AS data " + 
			"  FROM processos p " + 
			"  WHERE id_proces=?1 AND ambit='U' RETURNING id_instancia, id_proces " + 
			"), x AS( " + 
			"  INSERT INTO tasquesi(id_tascai, id_instancia, id_tasca, version) " + 
			"  (SELECT (y.id_instancia*10000 + id_tascap) *100, y.id_instancia, id_tasca, 0 " + 
			"  FROM tasques t, y " + 
			"  WHERE y.id_proces=t.id_proces " + 
			"  ORDER BY id_tascap) RETURNING id_tascai " + 
			") " + 
			"SELECT id_instancia FROM y;", nativeQuery=true)
	BigInteger instantiateU(Integer idProces, Integer titulacio);
	
    @Transactional
    @Modifying(flushAutomatically = true, clearAutomatically = true)
	@Query(value="WITH x AS(SELECT id_instancia FROM instancies WHERE id_instancia = ?1) " + 
			"INSERT INTO tasquesi(id_tascai, id_instancia, id_tasca, version) " + 
			"SELECT (x.id_instancia*10000 + id_tascap) *100, x.id_instancia, id_tasca, 0 " + 
			"FROM tasques t, x WHERE t.id_proces = ?2 ORDER BY id_tascap;", nativeQuery=true)
    void instantiateTasks(BigInteger idInstancia, Integer idProces);
    
    @Transactional
	@Query(value="UPDATE tasquesi SET estat='A', data=NOW() WHERE id_tascai IN(SELECT MIN(id_tascai) FROM tasquesi WHERE id_instancia = ?1) RETURNING id_tascai;", nativeQuery=true)
	BigInteger activateInstantiatedTask(BigInteger idInstancia);
    
    @Transactional
    @Modifying(flushAutomatically = true, clearAutomatically = true)
	@Query(value="DELETE FROM instancies WHERE id_instancia = ?1", nativeQuery=true)
    Integer deleteInstance(BigInteger idInstancia);
    
    @Transactional
    @Modifying(flushAutomatically = true, clearAutomatically = true)
	@Query(value="DELETE FROM tasquesi WHERE id_instancia = ?1", nativeQuery=true)
    Integer clearInstance(BigInteger idInstancia);
	
}

