var SNAPIHelper = {}
var snAPIHelper = {}

snAPIHelper.common = new class {
    executeInFrames(script) {
        var frames = document.getElementsByTagName("iframe");
        var message = {name: "exec", message: { script: script }};

        for (var index = 0; index < frames.length; index++) {
            frames[index].contentWindow.postMessage(message, "*");
        }
    }
}

// Message listener for relaying callbacks/events to iFrames
window.addEventListener('message', function(e) {
    console.log("Test", e);
    if (typeof e.data === "object") {
        if (e.data.name == "exec") {
            // Use safer indirect execution instead of eval (CSP compatible)
            try {
                // Parse the autofill function call pattern: snAPIHelper.autoFill.autoFill('username', 'password')
                const script = e.data.message.script;
                const match = script.match(/snAPIHelper\.autoFill\.autoFill\('([^']*)',\s*'([^']*)'\)/);
                if (match && snAPIHelper && snAPIHelper.autoFill) {
                    snAPIHelper.autoFill.autoFill(match[1], match[2]);
                }
            } catch (err) {
                console.error('[SNAPIHelper] Error executing iframe message:', err);
            }
        }
    }
}, false);


// MARK: -
var com_bzg_unite_del_key_disable = true;

SafariRemoteNotification = class {
    
}

// Unite Notification Permission System
window._uniteNotificationPermission = 'default';
window._uniteNotificationCallbacks = [];

// Function called from native code to update permission state
window.uniteSetNotificationPermission = function(permission) {
    window._uniteNotificationPermission = permission;
    // Resolve any pending permission callbacks
    while (window._uniteNotificationCallbacks.length > 0) {
        var cb = window._uniteNotificationCallbacks.shift();
        if (typeof cb === 'function') {
            cb(permission);
        }
    }
};

window.oldNotification = window.Notification;

window.Notification = function(title, options) {
    // Check if notifications are allowed before creating
    if (window._uniteNotificationPermission === 'denied') {
        console.log('[Unite] Notification blocked - permission denied');
        return {};
    }
    
    let webwhatsappWorkaroundURL = document.URL;
    if (webwhatsappWorkaroundURL.includes("https://web.whatsapp.com")) {
        //webwhatsappWorkaround
        window.webkit.messageHandlers.note.postMessage([JSON.stringify(title), JSON.stringify(options)]);
    } else {
        window.webkit.messageHandlers.notifications.postMessage([title, options]);
    }
    
    // Create a mock notification object with standard properties
    var notification = {
        title: title,
        body: options ? options.body : '',
        icon: options ? options.icon : '',
        tag: options ? options.tag : '',
        onclick: null,
        onclose: null,
        onerror: null,
        onshow: null,
        close: function() {}
    };
    return notification;
};

// Copy static properties from original Notification
window.Notification.prototype = window.oldNotification ? window.oldNotification.prototype : {};

Object.defineProperty(Notification, 'permission', {
    get() {
        return window._uniteNotificationPermission;
    }
});

Notification.requestPermission = function(callback) {
    // If already decided, return immediately
    if (window._uniteNotificationPermission !== 'default') {
        var perm = window._uniteNotificationPermission;
        if (typeof callback === 'function') {
            callback(perm);
            return undefined;
        } else {
            return Promise.resolve(perm);
        }
    }
    
    // Request permission from native code
    window.webkit.messageHandlers.notificationPermission.postMessage({
        type: 'requestPermission',
        origin: window.location.origin,
        hostname: window.location.hostname
    });
    
    // Handle callback vs promise API
    if (typeof callback === 'function') {
        window._uniteNotificationCallbacks.push(callback);
        return undefined;
    } else {
        return new Promise(function(resolve) {
            window._uniteNotificationCallbacks.push(resolve);
        });
    }
};

window.safari = {}
window.safari.pushNotification = new SafariRemoteNotification();

// MARK: -

snAPIHelper.autoFill = new class {
	constructor() {
		window.addEventListener('DOMContentLoaded', function() {
			var style = document.createElement('style');
			style.innerHTML = '\
			.SNAutofill {\
				background-color: #ffffdd !important;\
			}\
			';
			document.head.appendChild(style);
		}, false);
		
		this.KEYCODE_ARROW_DOWN = 40;
		this.lastDomUpdateTime = 0;
		this.domUpdatePending = false;
		this.THROTTLE_DELAY = 250; // ms
		
		// CRITICAL: Bind event handlers once to preserve 'this' context
		this.boundOnUsernameChange = this.onUsernameChange.bind(this);
		this.boundOnPasswordChange = this.onPasswordChange.bind(this);
		this.boundOnUsernameFocused = this.onUsernameFocused.bind(this);
		this.boundOnPasswordFocused = this.onPasswordFocused.bind(this);
		this.boundOnUsernameBlurred = this.onUsernameBlurred.bind(this);
		this.boundOnPasswordBlurred = this.onPasswordBlurred.bind(this);
		this.boundOnKeyUp = this.onKeyUp.bind(this);
		this.boundOnKeyDown = this.onKeyDown.bind(this);
		
		// Use event delegation for robustness - attach to document to catch all login fields
		this.setupEventDelegation();
	}
	
	setupEventDelegation() {
		// Helper to check if target is a login field (works with shadow DOM via composedPath)
		const getLoginFieldFromEvent = (event) => {
			if (!this.loginFields) return null;
			
			// Check composedPath for shadow DOM traversal
			const path = event.composedPath ? event.composedPath() : [event.target];
			
			for (let element of path) {
				if (element === this.loginFields["username"]) {
					return { field: 'username', element: element };
				}
				if (element === this.loginFields["password"]) {
					return { field: 'password', element: element };
				}
			}
			
			// Also check direct target
			if (event.target === this.loginFields["username"]) {
				return { field: 'username', element: event.target };
			}
			if (event.target === this.loginFields["password"]) {
				return { field: 'password', element: event.target };
			}
			
			return null;
		};
		
		// Focus events don't bubble, so use capture phase
		document.addEventListener('focus', (event) => {
			const field = getLoginFieldFromEvent(event);
			if (!field) return;
			
			if (field.field === 'username') {
				this.boundOnUsernameFocused(event);
			} else if (field.field === 'password') {
				this.boundOnPasswordFocused(event);
			}
		}, true); // useCapture = true for focus events
		
		// Input events
		document.addEventListener('input', (event) => {
			const field = getLoginFieldFromEvent(event);
			if (!field) return;
			
			if (field.field === 'username') {
				this.boundOnUsernameChange(event);
			} else if (field.field === 'password') {
				this.boundOnPasswordChange(event);
			}
		}, false);
		
		// Keydown events (for immediate response to typing)
		document.addEventListener('keydown', (event) => {
			const field = getLoginFieldFromEvent(event);
			if (field) {
				this.boundOnKeyDown(event);
			}
		}, false);
		
		// Keyup events
		document.addEventListener('keyup', (event) => {
			const field = getLoginFieldFromEvent(event);
			if (field) {
				this.boundOnKeyUp(event);
			}
		}, false);
		
		// Blur events
		document.addEventListener('blur', (event) => {
			const field = getLoginFieldFromEvent(event);
			if (!field) return;
			
			if (field.field === 'username') {
				this.boundOnUsernameBlurred(event);
			} else if (field.field === 'password') {
				this.boundOnPasswordBlurred(event);
			}
		}, true);
	}
	
	autoFill(username, password) {
		if (typeof this.loginFields !== "undefined") {
			if (this.loginFields["username"]) {
				// Mark field as being autofilled to prevent event handler interference
				this.loginFields["username"].setAttribute('data-autofilling', 'true');
				this.setInputValue(this.loginFields["username"], username);
				this.loginFields["username"].classList.add("SNAutofill");
				// Remove the flag after React has time to process
				setTimeout(() => {
					if (this.loginFields["username"]) {
						this.loginFields["username"].removeAttribute('data-autofilling');
					}
				}, 100);
			}

			if (this.loginFields["password"]) {
				// Mark field as being autofilled to prevent event handler interference
				this.loginFields["password"].setAttribute('data-autofilling', 'true');
				this.setInputValue(this.loginFields["password"], password);
				this.loginFields["password"].classList.add("SNAutofill");
				// Remove the flag after React has time to process
				setTimeout(() => {
					if (this.loginFields["password"]) {
						this.loginFields["password"].removeAttribute('data-autofilling');
					}
				}, 100);
			}
		}
		snAPIHelper.common.executeInFrames("snAPIHelper.autoFill.autoFill('"+username+"', '"+password+"')");
	}
	
	setInputValue(element, value) {
		// Set the value using multiple methods to ensure React/Vue/Angular detect the change
		
		// Method 1: Direct property assignment using native setter
		const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
		nativeInputValueSetter.call(element, value);
		
		// Method 2: Dispatch input event (required for React)
		const inputEvent = new Event('input', { bubbles: true, cancelable: true });
		element.dispatchEvent(inputEvent);
		
		// Method 3: Dispatch change event (for older frameworks)
		const changeEvent = new Event('change', { bubbles: true, cancelable: true });
		element.dispatchEvent(changeEvent);
		
		// Method 4: Keyboard events (for additional compatibility)
		this.dispatchKeyboardEvent(element, "keydown", this.KEYCODE_ARROW_DOWN);
		this.dispatchKeyboardEvent(element, "keyup", this.KEYCODE_ARROW_DOWN);
	}
	
	domUpdatedThrottled() {
		const now = Date.now();
		
		// Throttle updates to reduce CPU usage
		if (this.domUpdatePending || (now - this.lastDomUpdateTime) < this.THROTTLE_DELAY) {
			if (!this.domUpdatePending) {
				this.domUpdatePending = true;
				setTimeout(() => {
					this.domUpdatePending = false;
					this.lastDomUpdateTime = Date.now();
					this.domUpdated();
				}, this.THROTTLE_DELAY);
			}
			return;
		}
		
		this.lastDomUpdateTime = now;
		this.domUpdated();
	}
	
	domUpdated() {
		var loginFields = this.getLoginFields();
		
		if (typeof loginFields !== "undefined") {
			// Prevent re‑processing the same password element again.
			if (typeof this.loginFields !== "undefined") {
				var samePassword = (loginFields["password"] && this.loginFields["password"] && loginFields["password"] === this.loginFields["password"]);
				var sameUsername = (loginFields["username"] && this.loginFields["username"] && loginFields["username"] === this.loginFields["username"]);
				if (samePassword && sameUsername) {
					return;
				}
			}

			var usernameValue = (loginFields["username"] ? loginFields["username"].value : "");
			var passwordValue = (loginFields["password"] ? loginFields["password"].value : "");
			
			// Check if messageHandlers.autoFill exists before posting
			if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
				window.webkit.messageHandlers.autoFill.postMessage(["onLoginFound", { "username": usernameValue, "password": passwordValue }]);
			}

			// Remove old event listeners first (if fields were already tracked)
			if (this.loginFields && this.loginFields["username"]) {
				this.loginFields["username"].removeEventListener('input', this.boundOnUsernameChange, false);
				this.loginFields["username"].removeEventListener("focus", this.boundOnUsernameFocused, false);
				this.loginFields["username"].removeEventListener("blur", this.boundOnUsernameBlurred, false);
				this.loginFields["username"].removeEventListener("keydown", this.boundOnKeyDown, false);
				this.loginFields["username"].removeEventListener("keyup", this.boundOnKeyUp, false);
			}
			if (this.loginFields && this.loginFields["password"]) {
				this.loginFields["password"].removeEventListener('input', this.boundOnPasswordChange, false);
				this.loginFields["password"].removeEventListener("focus", this.boundOnPasswordFocused, false);
				this.loginFields["password"].removeEventListener("blur", this.boundOnPasswordBlurred, false);
				this.loginFields["password"].removeEventListener("keydown", this.boundOnKeyDown, false);
				this.loginFields["password"].removeEventListener("keyup", this.boundOnKeyUp, false);
			}
			
			// Add new event listeners with bound methods
			if (loginFields["username"]) {
				loginFields["username"].addEventListener('input', this.boundOnUsernameChange, false);
				loginFields["username"].addEventListener("focus", this.boundOnUsernameFocused, false);
				loginFields["username"].addEventListener("blur", this.boundOnUsernameBlurred, false);
				loginFields["username"].addEventListener("keydown", this.boundOnKeyDown, false);
				loginFields["username"].addEventListener("keyup", this.boundOnKeyUp, false);
			}
			if (loginFields["password"]) {
				loginFields["password"].addEventListener('input', this.boundOnPasswordChange, false);
				loginFields["password"].addEventListener("focus", this.boundOnPasswordFocused, false);
				loginFields["password"].addEventListener("blur", this.boundOnPasswordBlurred, false);
				loginFields["password"].addEventListener("keydown", this.boundOnKeyDown, false);
				loginFields["password"].addEventListener("keyup", this.boundOnKeyUp, false);
			}
		}
		this.loginFields = loginFields;
	}
	
	// Helper to search in shadow DOMs
	searchInShadowDOM(root, selector) {
		let results = Array.from(root.querySelectorAll(selector));
		
		// Search recursively in shadow roots
		root.querySelectorAll('*').forEach(el => {
			if (el.shadowRoot) {
				results = results.concat(this.searchInShadowDOM(el.shadowRoot, selector));
			}
		});
		
		return results;
	}
	
	getLoginFields() {
		// First, try to locate conventional username‑password pairs
		var passwordFields = this.allPasswordFields();
		var passwordLength = passwordFields.length;
		var loginGroups = [];
		while (passwordLength--) {
			var curPasswordField = passwordFields[passwordLength];
			var parentForm = this.parentForm(curPasswordField);

			var possibleUserField = null;
			if (parentForm) {
				possibleUserField = this.possibleUserField(parentForm, curPasswordField);
			} else {
				possibleUserField = this.possibleUserField(document, curPasswordField);
			}

			// It is possible that no explicit username field exists on the same page (e.g. multi‑step logins).
			// In that scenario, we still want to return the password field alone so that we can at least offer
			// password filling.
			if (possibleUserField) {
				loginGroups.push([possibleUserField, curPasswordField]);
			} else {
				loginGroups.push([null, curPasswordField]);
			}
		}

		if (loginGroups.length > 0) {
			// Prefer groups that have a visible username field; otherwise fall back to password‑only group.
			for (var i = 0; i < loginGroups.length; i++) {
				var u = loginGroups[i][0];
				var p = loginGroups[i][1];
				if (u && p && u.offsetParent !== null && p.offsetParent !== null) {
					return { "username": u, "password": p };
				}
			}
			// No visible username field – return the first password field we found.
			return { "username": null, "password": loginGroups[0][1] };
		}

                // Lastly, handle pages that only ask for username / email (first step of multi‑step flows).
                var inputs = Array.from(document.querySelectorAll('input[type="text"], input[type="email"], input[type="search"]'));
                
                // Search shadow DOMs for username fields
                var shadowInputs = this.searchInShadowDOM(document, 'input[type="text"], input[type="email"], input[type="search"]');
                inputs = inputs.concat(shadowInputs);
                
                if (inputs.length > 0) {
                        // Pick the first visible input field that doesn't appear to be a search field.
                        for (var i = 0; i < inputs.length; i++) {
                                if (inputs[i].offsetParent !== null) {
                                        var attrString = ((inputs[i].name || "") + (inputs[i].id || "") + (inputs[i].placeholder || "") + (inputs[i].getAttribute("aria-label") || "")).toLowerCase();
                                        var isSearch = inputs[i].type.toLowerCase() === "search" || attrString.indexOf("search") !== -1 || attrString.indexOf("query") !== -1;
                                        if (!isSearch) {
                                                return { "username": inputs[i], "password": null };
                                        }
                                }
                        }
                }
	}
	
	possibleUserField(parent, passwordField) {
		var inputFieldsByCategory = {};
		var inputs = parent.getElementsByTagName('input');
		var passwordFieldIndex = [].slice.call(inputs).indexOf(passwordField);
		var inputsByCategory = {};
		for (var i = 0; i < inputs.length; i++) {
			if (inputs[i] === passwordField || (passwordFieldIndex - i) < 0) {
				continue;
			}
			
			var fieldType = (inputs[i].hasAttribute("type") ?
							 inputs[i].getAttribute("type").toLowerCase() :
							 inputs[i].type);
			
			if (fieldType == "text"  ||
				fieldType == "email" ||
				fieldType == "url"   ||
				fieldType == "tel"   ||
				fieldType == "number") {
				if (typeof inputsByCategory[fieldType] === "undefined") {
					inputsByCategory[fieldType] = [];
				}
				
				inputsByCategory[fieldType].push([inputs[i], passwordFieldIndex - i]);
			}
		}
		
		if (typeof inputsByCategory["email"] !== "undefined") {
			return inputsByCategory["email"].sort(this.compare)[0][0];
		} else if (typeof inputsByCategory["text"] !== "undefined") {
			return inputsByCategory["text"].sort(this.compare)[0][0];
		}
	}
	
	allPasswordFields() {
		// Search regular DOM
		var inputs = Array.from(document.getElementsByTagName('input'));
		
		// Search shadow DOMs
		var shadowInputs = this.searchInShadowDOM(document, 'input');
		inputs = inputs.concat(shadowInputs);
		
		var	fields = [];
		for (let input of inputs) {
			if (input.type === 'password') {
				fields.push(input);
			}
		}
		return fields;
	}
	
	parentForm(element) {
		while (element.parentNode) {
			if (element.parentNode.nodeName.toLowerCase() === 'form') {
				return element.parentNode;
			}
			element = element.parentNode;
		}
	}
	
	compare(inputA, inputB) {
	  return (inputA[1] > inputB[1]);
	}
	
	onUsernameFocused(event) {
		// If field was autofilled, allow immediate editing
		if (event.target && event.target.classList && event.target.classList.contains("SNAutofill")) {
			event.target.classList.remove("SNAutofill");
			
			// Select all text so user can immediately type to replace
			setTimeout(() => {
				event.target.select();
			}, 0);
			
			// Don't show dropdown if user is editing an autofilled field
			return;
		}
		
		if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
			var viewportOffset = event.target.getBoundingClientRect();
			window.webkit.messageHandlers.autoFill.postMessage(["onUsernameFocused", {"top": viewportOffset.bottom, "left": viewportOffset.left}]);
		}
	}
	
	onUsernameBlurred(event) {
		if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
			window.webkit.messageHandlers.autoFill.postMessage(["onUsernameBlurred"]);
		}
	}
	
	onPasswordFocused(event) {
		// If field was autofilled, allow immediate editing
		if (event.target && event.target.classList && event.target.classList.contains("SNAutofill")) {
			event.target.classList.remove("SNAutofill");
			
			// Select all text so user can immediately type to replace
			setTimeout(() => {
				event.target.select();
			}, 0);
			
			// Don't show dropdown if user is editing an autofilled field
			return;
		}
		
		if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
			var viewportOffset = event.target.getBoundingClientRect();
			window.webkit.messageHandlers.autoFill.postMessage(["onPasswordFocused", {"top": viewportOffset.bottom, "left": viewportOffset.left}]);
		}
	}
	
	onPasswordBlurred(event) {
		if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
			window.webkit.messageHandlers.autoFill.postMessage(["onPasswordBlurred"]);
		}
	}
	
	onUsernameChange(event) {
		// Skip if we're in the middle of autofilling to avoid interference
		if (event.target && event.target.getAttribute('data-autofilling') === 'true') {
			return;
		}
		
		var viewportOffset = event.target.getBoundingClientRect();
		// Remove SNAutofill styling when user modifies the field
		if (event.target && event.target.classList) {
			event.target.classList.remove("SNAutofill");
		}
		if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
			window.webkit.messageHandlers.autoFill.postMessage(["onUsernameChange", event.target.value, {"top": viewportOffset.bottom, "left": viewportOffset.left}]);
		}
	}
	
	onPasswordChange(event) {
		// Skip if we're in the middle of autofilling to avoid interference
		if (event.target && event.target.getAttribute('data-autofilling') === 'true') {
			return;
		}
		
		var viewportOffset = event.target.getBoundingClientRect();
		// Remove SNAutofill styling when user modifies the field
		if (event.target && event.target.classList) {
			event.target.classList.remove("SNAutofill");
		}
		if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
			window.webkit.messageHandlers.autoFill.postMessage(["onPasswordChange", event.target.value, {"top": viewportOffset.bottom, "left": viewportOffset.left}]);
		}
	}
	
	onKeyDown(event) {
		// Remove autofill styling as soon as user starts typing
		// But don't interfere with the actual typing - let React handle it naturally
		if (event.target && event.target.classList && event.target.classList.contains("SNAutofill")) {
			event.target.classList.remove("SNAutofill");
		}
	}
	
	onKeyUp(event) {
		if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.autoFill) {
			if (event.keyCode === 13) {
				window.webkit.messageHandlers.autoFill.postMessage(["onKeyUp", 13]);
				event.preventDefault();
			} else if (event.keyCode === 27) {
				window.webkit.messageHandlers.autoFill.postMessage(["onKeyUp", 27]);
			} else if (event.key == "ArrowUp") {
				window.webkit.messageHandlers.autoFill.postMessage(["onKeyUp", -1]);
			} else if (event.key == "ArrowDown") {
				window.webkit.messageHandlers.autoFill.postMessage(["onKeyUp", 1]);
			}
		}
	}
	
	dispatchKeyboardEvent(element, eventName, keyCode) {
	  var event = document.createEvent("KeyboardEvent");
	  event.initKeyboardEvent(eventName, true, true, window, 0, 0, 0, 0, 0, keyCode);
	  element.dispatchEvent(event);
	}
}

// Optimized DOM observer - only watch for relevant changes
snAPIHelper.observer = new MutationObserver(function(mutationsList, observer) {
	let shouldUpdate = false;
	
	for (let mutation of mutationsList) {
		// Only care about added nodes and specific attribute changes
		if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
			for (let node of mutation.addedNodes) {
				if (node.nodeType === Node.ELEMENT_NODE) {
					// Check if added node contains form inputs
					if (node.tagName === 'INPUT' || node.tagName === 'FORM' || 
						node.querySelector('input[type="password"], input[type="text"], input[type="email"]')) {
						shouldUpdate = true;
						break;
					}
				}
			}
		} else if (mutation.type === 'attributes' && 
				   mutation.target.tagName === 'INPUT' && 
				   (mutation.attributeName === 'type' || mutation.attributeName === 'name')) {
			shouldUpdate = true;
		}
		
		if (shouldUpdate) break;
	}
	
	if (shouldUpdate) {
		snAPIHelper.autoFill.domUpdatedThrottled();
	}
});

// Only observe specific changes that matter for form detection
snAPIHelper.observer.observe(document, { 
	childList: true, 
	subtree: true,
	attributes: true,
	attributeFilter: ['type', 'name'] // Only watch for type/name changes on inputs
});

// Initial scan on page load to detect existing forms
if (document.readyState === 'loading') {
	document.addEventListener('DOMContentLoaded', function() {
		snAPIHelper.autoFill.domUpdated();
	});
} else {
	// DOM already loaded, scan immediately
	snAPIHelper.autoFill.domUpdated();
}

// Also scan after full page load (for SPAs and delayed forms)
window.addEventListener('load', function() {
	setTimeout(function() {
		snAPIHelper.autoFill.domUpdated();
	}, 500); // Small delay for dynamic content
});
