CREATE TABLE `vn`.`zoneExclusionGeo` (
  `zoneExclusionFk` int(11) NOT NULL,
  `geoFk` int(11) NOT NULL,
  PRIMARY KEY (`zoneExclusionFk`,`geoFk`),
  KEY `zoneExclusionGeo2_FK_1` (`geoFk`),
  CONSTRAINT `zoneExclusionGeo2_FK` FOREIGN KEY (`zoneExclusionFk`) REFERENCES `zoneExclusion` (`id`) ON UPDATE CASCADE,
  CONSTRAINT `zoneExclusionGeo2_FK_1` FOREIGN KEY (`geoFk`) REFERENCES `zoneGeo` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;


DROP PROCEDURE IF EXISTS `vn`.`zone_excludeFromGeo`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_excludeFromGeo`(vZoneGeo INT)
BEGIN
/**
 * Excluye zonas a partir un geoFk.
 *
 * @table tmp.zoneOption(zoneFk, hour, travelingDays, price, bonus, landed, shipped) The computed options
 * @param vZoneGeo The zone geo
 * @return tmp.zoneOption The computed options
 */	
	DELETE t FROM tmp.zoneOption t
		JOIN zoneExclusion e ON e.zoneFk = t.zoneFk AND e.`dated` = t.landed
		LEFT JOIN zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
		JOIN zoneGeo zg1 ON zg1.id = eg.geoFk
		JOIN zoneGeo zg2 ON zg2.id = vZoneGeo
	WHERE zg2.`path` LIKE CONCAT(zg1.`path`,'%');
END$$
DELIMITER ;


DROP PROCEDURE IF EXISTS `vn`.`zone_getEvents`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getEvents`(
	vGeoFk INT,
    vAgencyModeFk INT)
BEGIN
/**
 * Returns available events for the passed province/postcode and agency.
 *
 * @param vGeoFk The geo id
 * @param vAgencyModeFk The agency mode id
 */
	DECLARE vDeliveryMethodFk VARCHAR(255);
    
	DROP TEMPORARY TABLE IF EXISTS tZone;
	CREATE TEMPORARY TABLE tZone
		(id INT PRIMARY KEY)
		ENGINE = MEMORY;
        
	SELECT dm.`code` INTO vDeliveryMethodFk
		FROM agencyMode am
			JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
        WHERE am.id = vAgencyModeFk;
        
	IF vDeliveryMethodFk = 'PICKUP' THEN
		INSERT INTO tZone
			SELECT id
				FROM zone
                WHERE agencyModeFk = vAgencyModeFk;
	ELSE
		CALL zone_getFromGeo(vGeoFk);
		IF vAgencyModeFk IS NOT NULL THEN
			INSERT INTO tZone
				SELECT t.id
					FROM tmp.zone t
						JOIN zone z ON z.id = t.id
					WHERE z.agencyModeFk = vAgencyModeFk;
		ELSE
			INSERT INTO tZone
				SELECT t.id 
					FROM tmp.zone t
						JOIN zone z ON z.id = t.id
						JOIN agencyMode am  ON am.id = z.agencyModeFk 
						JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
				WHERE dm.`code` IN ('AGENCY', 'DELIVERY');
		END IF;
		DROP TEMPORARY TABLE tmp.zone;
	END IF;
    
	SELECT e.zoneFk, e.`type`, e.dated, e.`started`, e.`ended`, e.weekDays
		FROM tZone t
			JOIN zoneEvent e ON e.zoneFk = t.id;
			
	SELECT e.zoneFk, e.dated
		FROM tZone t
			JOIN zoneExclusion e ON e.zoneFk = t.id
			LEFT JOIN zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
			JOIN zoneGeo zg1 ON zg1.id = eg.geoFk
			JOIN zoneGeo zg2 ON zg2.id = vGeoFk
		WHERE eg.zoneExclusionFk IS NULL OR zg2.`path` LIKE CONCAT(zg1.`path`,'%');
            
	DROP TEMPORARY TABLE tZone;
END$$
DELIMITER ;

DROP PROCEDURE IF EXISTS `vn`.`zone_getLanded`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getLanded`(vShipped DATE, vAddressFk INT, vAgencyModeFk INT, vWarehouseFk INT, vShowExpiredZones BOOLEAN)
BEGIN
/**
 * Devuelve una tabla temporal con el dia de recepcion para vShipped.
 * Excluye las que tengan cajas preparadas
 *
 * @param vShipped Fecha de preparacion de mercancia
 * @param vAddressFk Id de consignatario, %NULL para recogida
 * @param vAgencyModeFk Id agencia
 * @param vWarehouseFk vWarehouseFk
 * @table tmp.zoneGetLanded Datos de recepciÃ³n
 */
	DECLARE vZoneGeo INT;
	SELECT address_getGeo(vAddressFk) INTO vZoneGeo;
	CALL vn.zone_getFromGeo(vZoneGeo);
	CALL vn.zone_getOptionsForShipment(vShipped, vShowExpiredZones);
	CALL vn.zone_excludeFromGeo(vZoneGeo);
	CALL vn.zone_getClosed();
	DROP TEMPORARY TABLE IF EXISTS tmp.zoneGetLanded;
	CREATE TEMPORARY TABLE tmp.zoneGetLanded
			ENGINE = MEMORY
		SELECT vWarehouseFk warehouseFk,
			TIMESTAMPADD(DAY,zo.travelingDays, vShipped) landed,
			zo.zoneFk
		FROM tmp.zoneOption zo
			JOIN vn.`zone` z ON z.id = zo.zoneFk
			JOIN vn.zoneWarehouse zw ON zw.zoneFk = z.id
			LEFT JOIN tmp.closedZones cz 
							ON cz.warehouseFk = zw.warehouseFk
								AND cz.zoneFk = zw.zoneFk
								AND zo.shipped = CURDATE()
		WHERE z.agencyModeFk = vAgencyModeFk
			AND zw.warehouseFk = vWarehouseFk
			AND (ISNULL(cz.zoneFk) OR vShowExpiredZones);
	DROP TEMPORARY TABLE
		tmp.`zone`,
		tmp.zoneOption,
		tmp.closedZones;
END$$
DELIMITER ;

DROP PROCEDURE IF EXISTS `vn`.`zone_getOptionsForLanding`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getOptionsForLanding`(vLanded DATE, vShowExpiredZones BOOLEAN)
BEGIN
/**
 * Gets computed options for the passed zones and delivery date.
 *
 * @table tmp.zone(id) The zones ids
 * @param vLanded The delivery date
 * @return tmp.zoneOption The computed options
 */	
	DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption;
	CREATE TEMPORARY TABLE tmp.zoneOption
		ENGINE = MEMORY
		SELECT
				zoneFk,
				`hour`,
				travelingDays,
				price,
				bonus,
				vLanded landed,
				TIMESTAMPADD(DAY, -travelingDays, vLanded) shipped
			FROM (
				SELECT t.id zoneFk,
						TIME(IFNULL(e.`hour`, z.`hour`)) `hour`,
						IFNULL(e.travelingDays, z.travelingDays) travelingDays,
						IFNULL(e.price, z.price) price,
						IFNULL(e.bonus, z.bonus) bonus
					FROM tmp.zone t
						JOIN zone z ON z.id = t.id
						JOIN zoneEvent e ON e.zoneFk = t.id
					WHERE (
							e.`type` = 'day'
							AND e.dated = vLanded
						) OR (
							e.`type` != 'day'
							AND e.weekDays & (1 << WEEKDAY(vLanded))
							AND (e.`started` IS NULL OR vLanded >= e.`started`)
							AND (e.`ended` IS NULL OR vLanded <= e.`ended`)
						)
					ORDER BY
						zoneFk,
						CASE
							WHEN e.`type` = 'day'
								THEN 1
							WHEN e.`type` = 'range'
								THEN 2
								ELSE 3
						END
			) t
			GROUP BY zoneFk;	
	
	DELETE t FROM tmp.zoneOption t
		JOIN zoneExclusion e ON e.zoneFk = t.zoneFk AND e.`dated` = t.landed
		LEFT JOIN zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
	WHERE eg.zoneExclusionFk IS NULL;
			
	IF NOT vShowExpiredZones THEN
		DELETE FROM tmp.zoneOption
			WHERE shipped < CURDATE()
				OR (shipped = CURDATE() AND CURTIME() > `hour`);
	END IF;
END$$
DELIMITER ;


DROP PROCEDURE IF EXISTS `vn`.`zone_getOptionsForShipment`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getOptionsForShipment`(vShipped DATE, vShowExpiredZones BOOLEAN)
BEGIN
/**
 * Gets computed options for the passed zones and shipping date.
 *
 * @table tmp.zones(id) The zones ids
 * @param vShipped The shipping date
 * @return tmp.zoneOption(zoneFk, hour, travelingDays, price, bonus, specificity) The computed options
 */
	DECLARE vHour TIME DEFAULT TIME(NOW());
		
	DROP TEMPORARY TABLE IF EXISTS tLandings;
	CREATE TEMPORARY TABLE tLandings
		(INDEX (eventFk))
		ENGINE = MEMORY
		SELECT e.id eventFk,
				@travelingDays := IFNULL(e.travelingDays, z.travelingDays) travelingDays,
				TIMESTAMPADD(DAY, @travelingDays, vShipped) landed
			FROM tmp.zone t
				JOIN zone z ON z.id = t.id
				JOIN zoneEvent e ON e.zoneFk = t.id;
	DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption;
	CREATE TEMPORARY TABLE tmp.zoneOption
		ENGINE = MEMORY
		SELECT *
			FROM (
				SELECT t.id zoneFk,
						TIME(IFNULL(e.`hour`, z.`hour`)) `hour`,
						l.travelingDays,
						IFNULL(e.price, z.price) price,
						IFNULL(e.bonus, z.bonus) bonus,
						l.landed,
						vShipped shipped
					FROM tmp.zone t
						JOIN zone z ON z.id = t.id
						JOIN zoneEvent e ON e.zoneFk = t.id
						JOIN tLandings l ON l.eventFk = e.id
					WHERE (
							e.`type` = 'day'
							AND e.`dated` = l.landed
						) OR (
							e.`type` != 'day'
							AND e.weekDays & (1 << WEEKDAY(l.landed))
							AND (e.`started` IS NULL OR l.landed >= e.`started`)
							AND (e.`ended` IS NULL OR l.landed <= e.`ended`)
						)
					ORDER BY
						zoneFk,
						CASE
							WHEN e.`type` = 'day'
								THEN 1
							WHEN e.`type` = 'range'
								THEN 2
								ELSE 3
						END
			) t
			GROUP BY zoneFk;
	DROP TEMPORARY TABLE tLandings;
	DELETE t FROM tmp.zoneOption t
		JOIN zoneExclusion e ON e.zoneFk = t.zoneFk AND e.`dated` = t.landed
		LEFT JOIN zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
	WHERE eg.zoneExclusionFk IS NULL;
	IF NOT vShowExpiredZones THEN
		DELETE FROM tmp.zoneOption
			WHERE vShipped < CURDATE()
				OR (vShipped = CURDATE() AND CURTIME() > `hour`);
	END IF;
END$$
DELIMITER ;


DROP PROCEDURE IF EXISTS `vn`.`zone_getShipped`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getShipped`(vLanded DATE, vAddressFk INT, vAgencyModeFk INT, vShowExpiredZones BOOLEAN)
BEGIN
/**
 * Devuelve la mínima fecha de envío para cada warehouse
 * Excluye aquellas zonas que ya tienen cajas preparadas en ese almacén
 *
 * @param vLanded La fecha de recepcion
 * @param vAddressFk Id del consignatario
 * @param vAgencyModeFk Id de la agencia
 * @return tmp.zoneGetShipped
 */
	DECLARE vZoneGeo INT;
	SELECT address_getGeo(vAddressFk) INTO vZoneGeo;
	
	CALL vn.zone_getFromGeo(vZoneGeo);
	CALL vn.zone_getOptionsForLanding(vLanded, vShowExpiredZones);
	CALL vn.zone_excludeFromGeo(vZoneGeo);
	CALL vn.zone_getClosed();
	DROP TEMPORARY TABLE IF EXISTS tmp.zoneGetShipped;
	CREATE TEMPORARY TABLE tmp.zoneGetShipped
			ENGINE = MEMORY
		SELECT * FROM (
				SELECT 	zo.zoneFk,
						zo.shipped,
						zo.`hour`,
						zw.warehouseFk,
						z.agencyModeFk,
						zo.price,
						zo.bonus
					FROM tmp.zoneOption zo
						JOIN vn.zoneWarehouse zw ON zw.zoneFk = zo.zoneFk
						JOIN vn.`zone` z ON z.id = zo.zoneFk
						LEFT JOIN tmp.closedZones cz 
							ON cz.warehouseFk = zw.warehouseFk
								AND cz.zoneFk = zw.zoneFk
								AND zo.shipped = CURDATE()
					WHERE z.agencyModeFk = vAgencyModeFk
						AND (ISNULL(cz.zoneFk) OR vShowExpiredZones)
				ORDER BY shipped) t
			GROUP BY warehouseFk;
	DROP TEMPORARY TABLE
		tmp.`zone`,
		tmp.zoneOption,
		tmp.closedZones;
END$$
DELIMITER ;


DROP PROCEDURE IF EXISTS `vn`.`zone_upcomingDeliveries`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_upcomingDeliveries`()
BEGIN
	DECLARE vForwardDays INT;
    
    SELECT forwardDays INTO vForwardDays FROM zoneConfig;
	CALL util.time_createTable(CURDATE(), DATE_ADD(CURDATE(), INTERVAL vForwardDays DAY));
	DROP TEMPORARY TABLE IF EXISTS tLandings;
	CREATE TEMPORARY TABLE tLandings
		(INDEX (eventFk))
		ENGINE = MEMORY
		SELECT e.id eventFk,
				@travelingDays := IFNULL(e.travelingDays, z.travelingDays) travelingDays,
				TIMESTAMPADD(DAY, @travelingDays, ti.dated) landed,
				ti.dated shipped
			FROM zone z
				JOIN zoneEvent e ON e.zoneFk = z.id
				JOIN tmp.time ti ON ti.dated BETWEEN curdate() AND TIMESTAMPADD(DAY, vForwardDays, curdate());
	DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption;			
	CREATE TEMPORARY TABLE tmp.zoneOption
		ENGINE = MEMORY
		SELECT *
			FROM (
				SELECT z.id zoneFk,
						TIME(IFNULL(e.`hour`, z.`hour`)) `hour`,
						l.travelingDays,
						IFNULL(e.price, z.price) price,
						IFNULL(e.bonus, z.bonus) bonus,
						l.landed,
						l.shipped
					FROM zone z
						JOIN zoneEvent e ON e.zoneFk = z.id
						JOIN tLandings l ON l.eventFk = e.id
					WHERE (
							e.`type` = 'day'
							AND e.`dated` = l.landed
						) OR (
							e.`type` != 'day'
							AND e.weekDays & (1 << WEEKDAY(l.landed))
							AND (e.`started` IS NULL OR l.landed >= e.`started`)
							AND (e.`ended` IS NULL OR l.landed <= e.`ended`)
						)
					ORDER BY
						zoneFk,
						CASE
							WHEN e.`type` = 'day'
								THEN 1
							WHEN e.`type` = 'range'
								THEN 2
								ELSE 3
						END
			) t
			GROUP BY zoneFk, landed;
	DELETE t FROM tmp.zoneOption t
		JOIN zoneExclusion e ON e.zoneFk = t.zoneFk AND e.`dated` = t.landed
		LEFT JOIN zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
	WHERE eg.zoneExclusionFk IS NULL;
			
	SELECT MAX(zo.`hour`) `hour`, zg.`name`, zo.shipped, zo.zoneFk
		FROM tmp.zoneOption zo
			JOIN `zone` z ON z.id = zo.zoneFk
			JOIN agencyMode am ON am.id = z.agencyModeFk
			JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
			JOIN zoneIncluded zi ON zi.zoneFk = z.id
            JOIN zoneGeo zg ON zg.id = zi.geoFk AND zg.type = 'province'
		WHERE dm.code = 'DELIVERY'
        GROUP BY shipped, zg.`name`
		ORDER BY shipped, zg.`name`;
	
    DROP TEMPORARY TABLE tmp.time, tLandings;
END$$
DELIMITER ;