DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`workerTimeControl_clockIn`(
		vWorkerFk INT,
		vTimed DATETIME,
		vDirection VARCHAR(10),
		vDevice VARCHAR(255)
)
BEGIN
/**
* Verifica si el empleado puede fichar
* @param vWorkerFk Identificador del trabajador
* @param vTimed Balor de la fichada, IF vTimed IS NULL vTimed = NOW
* @param vDirection Solo se pueden pasa los valores del campo
* 		workerTimeControl.direction ENUM('in', 'out', 'middle')
* @param vDevice Dispositivo en el que se ha fichado
* @return Si todo es correcto, retorna el número de id la tabla workerTimeControl.
* Si hay algún problema, devuelve el mesaje que se debe mostrar al usuario
* Solo retorna el primer problema, en caso de no ocurrir ningún error se añadirá
* fichada a la tabla vn.workerTimeControl
*/

	DECLARE vLastIn DATETIME;
	DECLARE vLastOut DATETIME;
	DECLARE vNextIn DATETIME;
	DECLARE vNextOut DATETIME;
	DECLARE vNextDirection ENUM('in', 'out');
	DECLARE vLastDirection ENUM('in', 'out');
	DECLARE vDayMaxTime INTEGER; 
	DECLARE vDayBreak INT;
	DECLARE vShortWeekBreak INT;
	DECLARE vLongWeekBreak INT;
	DECLARE vWeekScope INT;
	DECLARE vMailTo VARCHAR(50) DEFAULT NULL;
	DECLARE vUserName VARCHAR(50) DEFAULT NULL;
	DECLARE vIsError BOOLEAN DEFAULT FALSE;
	DECLARE vErrorMessage VARCHAR(255) DEFAULT NULL;
	DECLARE vErrorCode VARCHAR(50);
	DECLARE vDated DATE;
	DECLARE vIsAllowedToWork VARCHAR(50);
	DECLARE vIsManual BOOLEAN DEFAULT TRUE;
	DECLARE vMaxWorkShortCycle INT;
	DECLARE vMaxWorkLongCycle INT;

	DECLARE EXIT HANDLER FOR SQLSTATE '45000'
	BEGIN

	SELECT CONCAT(u.name, '@verdnatura.es'),
				CONCAT(w.firstName, ' ', w.lastName)
				INTO vMailTo, vUserName
			FROM account.user u
				JOIN worker w ON w.bossFk = u.id
			WHERE w.id = vWorkerFk;

		SELECT `description` INTO vErrorMessage
			FROM workerTimeControlError 
			WHERE `code` = vErrorCode;

		IF vErrorMessage IS NULL THEN
			SET vErrorMessage = 'Error sin definir';
		END IF;

		SELECT vErrorMessage `error`;
		SELECT CONCAT(vUserName,
				' no ha podido fichar por el siguiente problema: ',
				vErrorMessage)
			INTO vErrorMessage;
			
		CALL mail_insert( vMailTo, vMailTo, 'Error al fichar', vErrorMessage);
	END;

	IF (vTimed IS NULL) THEN
		SET vTimed = util.VN_NOW();
		SET vIsManual = FALSE;
	END IF;

	SET vDated = DATE(vTimed);

	SELECT IF(pc.code = 'driverCE',
				wc.dayBreakDriver,
				wc.dayBreak),
			wc.shortWeekBreak,
			wc.longWeekBreak,
			wc.weekScope,
			wc.dayMaxTime,
			wc.maxWorkShortCycle,
			wc.maxWorkLongCycle
			INTO vDayBreak,
			vShortWeekBreak,
			vLongWeekBreak,
			vWeekScope,
			vDayMaxTime,
			vMaxWorkShortCycle,
			vMaxWorkLongCycle
		FROM business b
			JOIN professionalCategory pc
				ON pc.id = b.workerBusinessProfessionalCategoryFk
			JOIN workerTimeControlConfig wc
		WHERE b.workerFk = vWorkerFk
			AND vDated BETWEEN b.started AND IFNULL(b.ended, vDated);
		
	-- CONTRATO EN VIGOR
	IF vDayBreak IS NULL THEN
		SET vErrorCode = 'INACTIVE_BUSINESS';
		CALL util.throw(vErrorCode);
	END IF;
	
	-- FICHADAS A FUTURO
	IF vTimed > util.VN_NOW() +  INTERVAL 1 MINUTE THEN
		SET vErrorCode = 'IS_NOT_ALLOWED_FUTURE';
		CALL util.throw(vErrorCode);
	END IF;
	
	-- VERIFICAR SI ESTÁ PERMITIDO TRABAJAR
	CALL timeBusiness_calculateByWorker(vWorkerFk, vDated, vDated);
	SELECT isAllowedToWork INTO vIsAllowedToWork
		FROM tmp.timeBusinessCalculate;
		DROP TEMPORARY TABLE tmp.timeBusinessCalculate;

	IF NOT vIsAllowedToWork THEN
		SET vErrorCode = 'IS_NOT_ALLOWED_WORK';
		CALL util.throw(vErrorCode);
	END IF;


	-- DIRECCION CORRECTA
	CALL workerTimeControl_direction(vWorkerFk, vTimed);
	IF (SELECT
			IF((IF(option1 IN ('inMiddle', 'outMiddle'),
					'middle',
					option1) <> vDirection
				AND IF(option2 IN ('inMiddle', 'outMiddle'),
					'middle',
					IFNULL(option2, '')) <> vDirection)
				OR (option1 IS NULL AND option2 IS NULL),
				TRUE ,
				FALSE)
			FROM tmp.workerTimeControlDirection
		) THEN
		SET vIsError = TRUE;
	END IF;
	
	
	IF vIsError THEN
		SET vErrorCode = 'WRONG_DIRECTION';
		IF(SELECT option1 IS NULL AND option2 IS NULL
			FROM tmp.workerTimeControlDirection) THEN
			
			SET vErrorCode = 'DAY_MAX_TIME';
		END IF;
		CALL util.throw(vErrorCode);
	END IF;
	DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
	-- FICHADAS IMPARES
	SELECT timed INTO vLastIn
		FROM workerTimeControl
		WHERE userFk = vWorkerFk
			AND direction = 'in'
			AND timed < vTimed
		ORDER BY timed DESC
		LIMIT 1;
	
	IF (SELECT IF(vDirection = 'in',
				MOD(COUNT(*), 2) ,
				IF (vDirection = 'out', NOT MOD(COUNT(*), 2), FALSE))
			FROM workerTimeControl
			WHERE userFk = vWorkerFk
				AND timed BETWEEN vLastIn AND vTimed
		) THEN
		SET vErrorCode = 'ODD_WORKERTIMECONTROL';
		CALL util.throw(vErrorCode);
	END IF;
	
	-- DESCANSO DIARIO
	SELECT timed INTO vLastOut
		FROM workerTimeControl
		WHERE userFk = vWorkerFk
			AND direction = 'out'
			AND timed < vTimed
		ORDER BY timed DESC
		LIMIT 1;
	
	SELECT timed INTO vNextIn
		FROM workerTimeControl
		WHERE userFk = vWorkerFk
			AND direction = 'in'
			AND timed > vTimed
		ORDER BY timed ASC
		LIMIT 1;
	
	CASE vDirection
		WHEN 'in' THEN
			IF UNIX_TIMESTAMP(vTimed) - UNIX_TIMESTAMP(vLastOut) <= vDayBreak THEN
				SET vIsError = TRUE;
			END IF;
		WHEN 'out' THEN
			IF UNIX_TIMESTAMP(vNextIn) - UNIX_TIMESTAMP(vTimed) <= vDayBreak THEN
				SET vIsError = TRUE;
			END IF;
		ELSE BEGIN END;
	END CASE;

	IF vIsError THEN
		SET vErrorCode = 'BREAK_DAY';
		CALL util.throw(vErrorCode);
	END IF;



	IF (vDirection IN('in', 'out')) THEN
		-- VERIFICA MAXIMO TIEMPO DESDE ENTRADA HASTA LA SALIDA
		
		SELECT timed INTO vNextOut
			FROM workerTimeControl
			WHERE userFk = vWorkerFk
				AND direction = 'out'
				AND timed > vTimed
			ORDER BY timed ASC
			LIMIT 1;
	
		SELECT direction INTO vNextDirection
			FROM workerTimeControl
			WHERE userFk = vWorkerFk
				AND direction IN('in','out')
				AND timed > vTimed
			ORDER BY timed ASC
			LIMIT 1;
		
		SELECT direction INTO vLastDirection
			FROM workerTimeControl
			WHERE userFk = vWorkerFk
				AND direction IN('in', 'out')
				AND timed < vTimed
			ORDER BY timed ASC
			LIMIT 1;
	
		IF (vDirection ='in' 
				AND vNextDirection = 'out' 
				AND UNIX_TIMESTAMP(vNextOut) - UNIX_TIMESTAMP(vTimed) > vDayMaxTime) OR
			(vDirection ='out' 
				AND vLastDirection = 'in'
				AND UNIX_TIMESTAMP(vTimed) -UNIX_TIMESTAMP(vLastIn) > vDayMaxTime) THEN
			SET vErrorCode = 'DAY_MAX_TIME';
			CALL util.throw(vErrorCode);
		END IF;
		
		-- VERIFICA DESCANSO SEMANAL
	
		WITH wtc AS(
		(SELECT timed 
			FROM vn.workerTimeControl 
			WHERE userFk = vWorkerFk
				AND direction IN ('in', 'out')
				AND timed BETWEEN vTimed - INTERVAL (vWeekScope * 2) SECOND 
					AND vTimed + INTERVAL (vWeekScope * 2) SECOND )
		UNION 
		(SELECT vTimed)
		), wtcGap AS(
			SELECT timed,
				TIMESTAMPDIFF(SECOND, LAG(timed) OVER (ORDER BY timed), timed) gap 
			FROM wtc
			ORDER BY timed
		), wtcBreak AS( 
			SELECT timed,
					IF(IFNULL(gap, 0) > vShortWeekBreak, TRUE, FALSE) hasShortBreak,
					IF(IFNULL(gap, 0) > vLongWeekBreak, TRUE, FALSE) hasLongBreak
				FROM wtcGap
				ORDER BY timed
		), wtcBreakCounter AS(
			SELECT timed,
					SUM(hasShortBreak) OVER (ORDER BY timed) breakCounter ,
					LEAD(hasLongBreak) OVER (ORDER BY timed) nextHasLongBreak
				FROM wtcBreak
		)SELECT TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed)) > vMaxWorkLongCycle OR 
				(TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed))> vMaxWorkShortCycle 
					AND NOT SUM(IFNULL(nextHasLongBreak, 1)))
				hasError INTO vIsError
			FROM wtcBreakCounter
			GROUP BY breakCounter
			HAVING hasError
			LIMIT 1;

		IF vIsError THEN
			SET vErrorCode = 'BREAK_WEEK';
			CALL util.throw(vErrorCode);
		END IF;
	END IF;

	-- Se permite fichar
	INSERT INTO workerTimeControl(userFk, timed, direction, device, `manual`)
		VALUES(vWorkerFk, vTimed, vDirection, vDevice, vIsManual);

	SELECT LAST_INSERT_ID() id;

END$$
DELIMITER ;