/**
 * the top level controller
 */
angular.module("sn.$sp").controller("spPageCtrl", function($scope, $http, $location, $window, spAriaUtil, spUtil, spMetatagService, spAnnouncement,
	snRecordWatcher, $rootScope, spPage, spAriaFocusManager, $timeout, spAtf, spGtd, spContextManager, snAnalyticsUtil, snAnalytics, tinymceService) {
	'use strict';

	var _ = $window._;

	var c = this;
	var hasDynamicHeaderFooter = true;

	c.doAnimate = false; // Used to prevent animation on the first page load
	c.firstPage = true; // @todo: Instead of having a global flag, we should fire an event once.
	c.loadingIndicator = false;
	$scope.theme = {};
	$scope.page = {
		title: "Loading..."
	}; //@todo: This string needs to allow translation.

	$scope.sessions = {}; //@todo: that needs to be in a Factory. Check 'sessions' widget.

	$scope.$on('sp_loading_indicator', function(e, value) {
		c.loadingIndicator = value;
	});

	if ($window.NOW.sp_show_console_error) {
		spPage.showBrowserErrors();
	}

	// This is used for the ng-style on containers
	c.parseJSON = function(str) {
		return JSON.parse(str);
	};

	c.isObjectEmpty = function(obj) {
		if (obj && typeof (obj) === 'object')
			return Object.keys(obj).length === 0;
	}

	c.getContainerClasses = function(container) {
		var classes = [];

		if (!container.bootstrap_alt) {
			classes[classes.length] = container.width;
		}

		if (container.container_class_name) {
			classes[classes.length] = container.container_class_name;
		}

		return classes;
	};

	/***
	 * first page load and subsequent location changes
	 */

	var oid = $location.search().id;
	var oldPath = $location.path();
	var locationChanged = false;

	function isNavigateWithinSamePortal(newUrlPath, oldUrlPath) {

        var newUrlSlashIndex = newUrlPath.indexOf('/', 1),
            oldUrlSlashIndex = oldUrlPath.indexOf('/', 1);

        if (newUrlSlashIndex > -1)
            newUrlPath = newUrlPath.substring(0, newUrlSlashIndex);

        if (oldUrlSlashIndex > -1)
            oldUrlPath = oldUrlPath.substring(0, oldUrlSlashIndex);

		return newUrlPath === oldUrlPath ? true: false;
	}

	function isNavigateOutPortal(newUrl, oldUrl) {
		var newUrlParser = document.createElement('a'),
				oldUrlParser = document.createElement('a');

		newUrlParser.href = newUrl;
		oldUrlParser.href = oldUrl;

		//navigate to external url
		if (newUrlParser.hostname !== oldUrlParser.hostname)
			return true;

		//navigate inside same portal
		if (isNavigateWithinSamePortal(newUrlParser.pathname, oldUrlParser.pathname))
			return false;

		//navigate to out from current portal
		return true;
	}

	$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
		if (isNavigateOutPortal(newUrl, oldUrl)) {
			event.preventDefault();
			$window.location = newUrl;
		}
	});

	function getSearchParams(url) {
		var searchStart = url.indexOf("?");
		var searchMap = [];
		if (searchStart > -1){
			url = url.substring(searchStart + 1);
			var queryParams = url.split('&');
			queryParams.forEach(function(qp) {
				searchMap.push(qp.split("="));
			});
		}
		searchMap.sort(function(qp1, qp2){
			qp1[0] < qp2[0]; 
		});
		return searchMap; // [["id", "kb_article"], ["sys_id", "24d9243187032100deddb882a2e3ec33"]]
	}

	function isSamePage(newUrl, oldUrl) {
		var oldSearchMap = getSearchParams(oldUrl);
		var newSearchMap = getSearchParams(newUrl);

		if (oldSearchMap.length != newSearchMap.length)
			return false;

		for(var i = 0; i < oldSearchMap.length; i++) {
			if(oldSearchMap[i][0] != newSearchMap[i][0] || oldSearchMap[i][1] != newSearchMap[i][1])
				return false;
		}
		return true;
	}

	$rootScope.$on('$locationChangeSuccess', function(e, newUrl, oldUrl) {
		if (!c.firstPage && isSamePage(newUrl, oldUrl)) {
			e.preventDefault();
			return;
		}
		
		locationChanged = (oldUrl != newUrl);
		var s = $location.search();
		var p = $location.path();

		// Has portal changed?
		if (!isNavigateWithinSamePortal(oldPath, p)) {
			$window.location.href = $location.absUrl();
			return;
		}

		// page is handling?
		if (angular.isDefined($scope.containers) && oid == s.id && s.spa) {
			return;
		}

		if (spPage.isHashChange(newUrl, oldUrl)) {
			return;
		}

		if (!g_persist_msgs_through_page_nav)
			$scope.$broadcast('$$uiNotification.dismiss');
		if (newUrl = spPage.containsSystemPage(p)) {
			$window.location.href = newUrl;
			return;
		}

		// Redirect if the current page is the login page
		if (!$window.NOW.has_access && locationChanged) {
			$window.location.href = $location.absUrl();
			return;
		}

		oid = s.id;

		getPage();
	});

	$rootScope.$on('sp.page.loaded', function() {
		if(spUtil.isMobile())
			spUtil.setMobileBanner($scope);
	});

	function updateURLSilently(url) {
		$location.path(url).replace();
	}

	function loadPage(r) {
		var response = r.data.result;

		if (response.page && NOW.portal_url_suffix != 'null') {
			var hrPath = decodeURI(response.page.human_readable_url.toLowerCase());
			if( $location.path().toLowerCase() != hrPath)
				updateURLSilently(NOW.portal_url_suffix + (hrPath != "" ? ("/" + g_lang + "/" + hrPath) : ""));
		}

		spMetatagService.setTags(response.metatags);
		spMetatagService.setSeoTags(response.seotags);

		c.firstPage = false;

		$scope.containers = _.filter(response.containers, {
		  'subheader': false
		});

		$scope.subheaders = _.filter(response.containers, {
			'subheader': true
		});

		// promote to root
		var p = response.page;
		var u = response.user;

		/*
		 * All initial page loads already handle authentication (showing login page)
		 * so if $location has changed and page is not public, reload to show the login page.
		 * That way, the URL stays the same, and user will see correct page after login.
		 */
		if (!spPage.isPublicOrUserLoggedIn(p, u)) {
			if (locationChanged) {
				$window.location.href = $location.absUrl();
				return;
			}
		}

		if(r.config && r.config.url) {
			response.page.url = $window.location.origin.concat(r.config.url);
		}
		$rootScope.page = $scope.page = p;

		$(spPage.getElement(p)).remove(); // Removes Page CSS
		$(spPage.getStyle(p)).appendTo('head'); // Adds Page CSS

		response.portal = $rootScope.portal;
		$window.document.title = spPage.getTitle(response);

		$scope.$broadcast('$sp.scroll', {position: 0}); // Scroll to the Top of the page

		//@todo: Cleanup variable trains
		//STRY50668467: By avoiding header/footer server script calls, empty theme will be returned from server response from second page load request onwards. Handling the same with this condition.
		if (response.theme) {
			var theme = response.theme;
			$rootScope.theme = $scope.theme = theme;
			var isHeaderDynamic = theme.header && (theme.header['static'] === false);
			var isFooterDynamic = theme.footer && (theme.footer['static'] === false);
			hasDynamicHeaderFooter = !!(isHeaderDynamic || isFooterDynamic);
		}

		c.style = spPage.getClasses($scope);

		if (!$scope.user) {
			$rootScope.user = $scope.user = {};
		}

		$scope.g_accessibility = spAriaUtil.g_accessibility;

		angular.extend($scope.user, response.user);
		$scope.user.logged_in = spPage.userLoggedIn($scope.user);
		$scope.$broadcast('$$uiNotification', response.$$uiNotification);
		
		if ($scope.user.logged_in && !p.omit_watcher)
			snRecordWatcher.init();

		$timeout(function() {
			c.doAnimate = true;
		}, 500);

		//gtd specific code
		
		//PRB1402276: Guided Tours on Service Portal will auto-launch even when the device screen size is within the mobile range
		if (spUtil.isMobile()) {
			 NOW.sp.enableTours = false;
		}
		
		if (NOW && NOW.sp && NOW.sp.enableTours && $scope.user.logged_in) {
			spGtd.getToursForPage({portal: $rootScope.portal, page: $rootScope.page, user: $rootScope.user})
			.then(function(data) {
				$rootScope.$broadcast('sp-menu-update-tours', data);
				//for cases when the sp-header has not yet loaded
				$scope.$on('sp-header-loaded', function() {
					$rootScope.$broadcast('sp-menu-update-tours', data);
				});
			});
		}

		spContextManager.init();

		var recordInfo = {};
		var queryParams = $location.search();
		Object.keys(queryParams).forEach(function (key) {
			if (key === 'table' || key === 'sys_id')
				recordInfo[key] = queryParams[key];
		});

		if (recordInfo.table || recordInfo.sys_id)
			spContextManager.updateContextForKey('record', recordInfo);

		sendAnalytics(response.page);
		return r;
	}

	function sendAnalytics(page) {
		snAnalytics.startPage(page.id, page.static_title);
	}
	
	$rootScope.$on('sn.ucm.finished', function() {
		// just after login when page loads we do call startPage but it will fail because of analytic checks (ucm has to be finished to pass analytic checks)
		// so we need to listen to 'sn.ucm.finished' event so that we can send the initial page payload to appsee :)
		sendAnalytics($scope.page);

		if (NOW.ucm_invocations === 1) {
			// send successful login event
			// Make sure event is sent after page is set so no empty pages will be marked on login
			var payload= {};
			payload.name = "Successful Login";
			payload.data = {};
			payload.data["Login"] = "true";
			snAnalytics.addEvent(payload);
		}
	});

	function setupAtf() {
		spAtf.init().then(function(atf) {
			atf.triggerPageLoaded();
		});
	}

	function signalPageLoaded() {
		$rootScope.$emit('sp.page.loaded', $rootScope);
		displayAnalyticsConsentModal();
	}
	
	function displayAnalyticsConsentModal() {
		// return if sp_config portal
		if ($rootScope.portal && $rootScope.portal.sys_id === 'db57a91047001200ba13a5554ee49050')
			return;
		
		// once page is loaded invoke UCM egnine which decides whether to display consent modal to user or not
		snAnalyticsUtil.invokeUCMEngine();
	}

	function getSPPageResponse() {
		if (NOW.spPageResponse) {
			return NOW.spPageResponse.then(function(response) {
				return {
					data: response
				}
			});
		} else {
			return $http({
				method: 'GET',
				url: spPage.getUrl($scope.portal_id, !hasDynamicHeaderFooter),
				headers: spUtil.getHeaders()
			})
		}
	}

	function getPage() {
		return getSPPageResponse()
		.then(loadPage, handlePageLoadErrors)
		.then(function(res) {
			// Forcing announcements to load with the page
			spAnnouncement.init(res.data.result.announcements).then(function() {
				spAriaFocusManager.pageLoadComplete($location.url());
				setupAtf();
			});
		})
		.then(signalPageLoaded)
		.then(function() {
			NOW.spPageResponse = null;
		});
	}

	function handlePageLoadErrors(error) {
		var absUrl = $location.absUrl();
		var url = $location.url();
		var path = $location.path();
		// PRB1389764 : For unauthorized exceptions refreshing url with redirect URL as portal page
		if ('Unauthorized' == error.statusText || '401' == error.status) {
			absUrl = absUrl.replace(url, path + '?sysparm_goto_url='+$rootScope.portal.url_suffix);
			$window.location.href = absUrl;
		}
		//For other than unauthorized errors, intimating js error in the console.
		console.error(error);
		return Promise.reject(error);
	}

	$scope.$on('sp.page.reload', getPage);

	// keyboard shortcut to save form - broadcasts out $sp.save
	$($window).keydown(spPage.saveOnCtrlS);

	$scope.$on('$destroy', function() {
		$($window).off('keydown', spPage.saveOnCtrlS);
	});

	c.focusOnPageTitle = function(focusFirstTabbableEl, $event) {
		spAriaFocusManager.focusOnPageTitle(focusFirstTabbableEl, $event);
	}

	c.focusOnAgentChat = function() {
		spAriaFocusManager.focusOnAgentChat();
	}
	
	spAriaUtil.init();

	/**
	  * Defer load SP scripts part of `js_includes_sp_defer`
	  */
	function deferScripts(wait) {
		var delay = wait || 0;
		$timeout(function() {
			var element = document.createElement('script');
			element.src = 'scripts/js_includes_sp_defer.js?v=' + g_builddate;
			element.onload = function() {
				$rootScope.$broadcast("sp.defer_scripts.loaded");
			};
			document.body.appendChild(element);
			tinymceService.loadTinymceAsync();
		}, delay);
	}
	if (!$window.logged_in) {
		deferScripts(7000); // To improve SEO metrics for public pages
	} else {
		deferScripts();
	}
});
