import { AppRequest } from './models';
import { IApp } from './interfaces';
declare global {
    interface Window {
        App: IApp;
        Sentry: any;
        select: any;
    }
}


function getMeta(name) {
    const meta = document.querySelector('meta[name="' + name + '"]');

    if (meta) {
        return meta.getAttribute('content');
    } else {
        return '#FFFFFFFF';
    }
}

const v1 = !/\/2\.\d\.\d/i.test(navigator.userAgent);
const isAndroid = /android/i.test(navigator.userAgent);
if (typeof window.App !== 'undefined') {
    if (!window.App.on) {
        const appDebug = localStorage && localStorage.getItem('appDebug') === 'true';
        let requestId = 0;
        const handlers: ((...args) => boolean | void)[][] = [];
        const requests: AppRequest[] = [];
        window.App = new Proxy(window.App, {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            get(target: any, propKey: string) {
                if (propKey === 'isWeb') {
                    return false;
                }
                return (...args) => {
                    if (window.Sentry && appDebug) {
                        window.Sentry.captureMessage(propKey, {
                            extra: args,
                            level: 'info',
                        });
                    }
                    if (!v1 && propKey === 'shareFile') {
                        propKey = 'share';
                        args = [{ 'url': args[0] }];
                    }
                    if (propKey === 'emit') {
                        const channel = args.shift();
                        if (channel in handlers) {
                            try {
                                let success = true;
                                for (const handler of handlers[channel]) {
                                    success = success && handler(...args) !== false;
                                }
                                return success;
                            } catch (e) {
                                return false;
                            }
                        } else {
                            return false;
                        }
                    } else if (propKey === 'onSuccess' || propKey === 'onError' || propKey === 'resolve' || propKey === 'reject') {
                        const request = requests[args[0]];
                        if (request) {
                            if (args.length > 1) {
                                return request[propKey](args[1]);
                            } else {
                                return request[propKey]();
                            }
                        } else {
                            // eslint-disable-next-line max-len, no-console
                            console.error('Unable to resolve request. (id=' + args[0] + ', now=' + Date.now() + ', data=' + JSON.stringify(args[1]) + ')');
                        }
                    } else if (target[propKey] === undefined) {
                        if (propKey === 'on') {
                            if (!handlers[args[0]]) {
                                handlers[args[0]] = [];
                            }
                            handlers[args[0]].push(args[1]);
                            if (v1) {
                                target.postMessage(JSON.stringify({ 'listening': args[0] }));
                                return;
                            }
                        }
                        const id = 'req-' + (requestId++) + '-' + Date.now();
                        if (v1) {
                            return new Promise((resolve, reject) => {
                                requests[id] = new AppRequest(id, propKey, resolve, reject);
                                target.postMessage(JSON.stringify({ 'id': id, 'method': propKey, 'params': args }));
                            });
                        } else if (!isAndroid) {
                            return target.postMessage(JSON.stringify({ method: propKey, params: args })).then((result) => {
                                if (result && result.compatError) {
                                    throw result.compatError;
                                } else {
                                    return result;
                                }
                            });
                        } if (target.request) {
                            return new Promise((resolve, reject) => {
                                requests[id] = { resolve, reject };
                                target.request(propKey, id, JSON.stringify(args));
                            });
                        } else {
                            return new Promise((resolve, reject) => {
                                requests[id] = { resolve, reject };
                                target.postMessage(propKey, id, JSON.stringify(args));
                            });
                        }
                    }
                };
            },
        });

        const select = function (params: any) {
            return App.select(params).then((files: any) => {
                const dataTransfer = new DataTransfer();
                for (const file of files) {
                    const bytes = atob(file.data);
                    let length = bytes.length;
                    const out = new Uint8Array(length);

                    // Loop and convert.
                    while (length--) {
                        out[length] = bytes.charCodeAt(length);
                    }
                    dataTransfer.items.add(new File([out], file.name, { type: file.type }));
                }
                return dataTransfer;
            });
        };

        // Bypass native browser functionalities
        if (v1) {
            window.alert = window.App.alert;
            window.confirm = (message) => {
                let result;
                const promise = window.App.confirm(message);
                promise.then((res) => result = res);
                while (result === undefined) {
                    //
                }
                return result;
            };


            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            window.print = () => window.App.printHtml(document.documentElement.outerHTML);

            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            navigator.setAppBadge = window.App.setAppBadge;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            navigator.clearAppBadge = window.App.clearAppBadge;

            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            navigator.vibrate = window.App.vibrate;

            if (!('share' in navigator)) {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                navigator.share = (data) => window.App.share(data?.text ?? data?.url);
            }

            if (!('permissions' in navigator)) {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                navigator.permissions = {};
            }
            navigator.permissions.query = window.App.hasPermission;

            if (!('clipboard' in navigator)) {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                navigator.clipboard = {};
            }
            navigator.clipboard.readText = window.App.paste;
            navigator.clipboard.writeText = window.App.copy;

            if (!('geolocation' in navigator)) {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                navigator.geolocation = {};
            }
            const positionCallbacks: (PositionCallback | undefined)[] = [];
            window.App.on('position', (location) => {
                if (positionCallbacks.length === 0) {
                    window.App.watchPosition(false, undefined);
                } else {
                    positionCallbacks.forEach((callback) => {
                        if (callback) {
                            callback({
                                timestamp: location.time,
                                coords: {
                                    latitude: location.latitude,
                                    longitude: location.longitude,
                                    accuracy: location.accuracy,
                                    altitude: location.altitude,
                                    altitudeAccuracy: location.headingAccuracy,
                                    heading: location.heading,
                                    speed: location.speed,
                                },
                            });
                        }
                    });
                }
            });
            navigator.geolocation.watchPosition = (callback, errorCallback, options) => {
                const watchId = positionCallbacks.length;
                let hasListeners = false;
                positionCallbacks.forEach((element) => {
                    if (element) {
                        hasListeners = true;
                    }
                });
                positionCallbacks[watchId] = callback;
                if (!hasListeners) {
                    window.App.watchPosition(true, options).then((succeed) => {
                        if (!succeed && errorCallback) {
                            errorCallback({
                                code: 0,
                                message: 'Unable to start service',
                                PERMISSION_DENIED: 1,
                                POSITION_UNAVAILABLE: 2,
                                TIMEOUT: 3,
                            });
                        }
                    }).catch((error) => {
                        if (errorCallback) {
                            let code = 0;
                            switch (error.code) {
                                case 'PERMISSION_DENIED': code = 1; break;
                                case 'POSITION_UNAVAILABLE': code = 2; break;
                                case 'TIMEOUT': code = 3; break;
                            }
                            errorCallback({
                                code: code,
                                message: error.message,
                                PERMISSION_DENIED: 1,
                                POSITION_UNAVAILABLE: 2,
                                TIMEOUT: 3,
                            });
                        }
                    });
                }
                return watchId;
            };
            navigator.geolocation.clearWatch = (watchId) => {
                positionCallbacks[watchId] = undefined;
                let hasListeners = false;
                positionCallbacks.forEach((element) => {
                    if (element) {
                        hasListeners = true;
                    }
                });
                if (!hasListeners) {
                    window.App.watchPosition(false, undefined);
                }
            };
            navigator.geolocation.getCurrentPosition = (callback, errorCallback, settings) => {
                window.App.getCurrentPosition(settings).then((location) => callback({
                    timestamp: location.time,
                    coords: {
                        latitude: location.latitude,
                        longitude: location.longitude,
                        accuracy: location.accuracy,
                        altitude: location.altitude,
                        altitudeAccuracy: location.headingAccuracy,
                        heading: location.heading,
                        speed: location.speed,
                    },
                })).catch((error) => {
                    if (errorCallback) {
                        let code = 0;
                        switch (error.code) {
                            case 'PERMISSION_DENIED': code = 1; break;
                            case 'POSITION_UNAVAILABLE': code = 2; break;
                            case 'TIMEOUT': code = 3; break;
                        }
                        errorCallback({
                            code: code,
                            message: error.message,
                            PERMISSION_DENIED: 1,
                            POSITION_UNAVAILABLE: 2,
                            TIMEOUT: 3,
                        });
                    }
                });
            };

            if (!('Notification' in window)) {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                window.Notification = {
                    constuct(title, options) {
                        window.App.alert(title)
                    }
                };
            }
            // eslint-disable-next-line max-len
            window.Notification.requestPermission = () => window.App.requestNotificationPermission().then((allowed) => allowed ? 'granted' : 'denied');

            window.select = () => new Promise((resolve, reject) => resolve([]))
        } else if (isAndroid) {
            window.print = () => window.App.print();
            navigator.share = (data) => window.App.share(data);
            navigator.clipboard.writeText = (data) => window.App.copy(data);
            navigator.clipboard.readText = () => window.App.paste();
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            navigator.setAppBadge = (data) => window.App.setAppBadge(data);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            navigator.clearAppBadge = () => window.App.clearAppBadge();
            navigator.vibrate = (data) => window.App.vibrate(data);
            window.open = (url, name) => window.App.openUrl(url, name);

            if (!('permissions' in navigator)) {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                navigator.permissions = {};
            }
            navigator.permissions.query = window.App.hasPermission;
            window.select = select
        } else {
            const positionCallbacks: any[] = [];
            window.print = () => window.App.print();
            window.open = (url, name) => window.App.openUrl(url, name);
            navigator.share = (data) => window.App.share(data);
            navigator.clipboard.writeText = (data) => window.App.copy(data);
            navigator.clipboard.readText = () => window.App.paste();
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            navigator.setAppBadge = (data) => window.App.setAppBadge(data);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            navigator.clearAppBadge = () => window.App.clearAppBadge();
            navigator.vibrate = (data) => window.App.vibrate(data);

            window.App.on('position', location => {
                if (positionCallbacks.length) {
                    positionCallbacks.forEach(obj => obj.callback(location));
                } else {
                    window.App.watchPosition(false);
                }
            })
            navigator.geolocation.watchPosition = (callback, errorCallback, options) => {
                const watchId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
                if (!positionCallbacks.length) {
                    window.App.watchPosition(true, options).then(callback).catch(errorCallback);
                }
                positionCallbacks.push({
                    id: watchId,
                    callback,
                });
                return watchId
            }
            navigator.geolocation.clearWatch = (watchId) => {
                positionCallbacks.filter((cb, i, arr) => {
                    if (cb.id == watchId) {
                        arr.splice(i, 1);
                        return true;
                    } else {
                        return false;
                    }
                })
                if (!positionCallbacks.length) {
                    window.App.watchPosition(false);
                }
            }
            navigator.geolocation.getCurrentPosition = (callback, errorCallback, settings) => window.App.getCurrentPosition(settings).then(callback).catch(errorCallback)

            window.select = select
        }
    }

    document.addEventListener('DOMContentLoaded', () => {
        try {
            const viewport = getMeta('viewport');
            const keyboardResize = !!viewport && viewport.includes('interactive-widget=resizes-content');
            const appBarOffset = !!viewport && !viewport.includes('viewport-fit=cover');
            const navBarOffset = !!viewport && !viewport.includes('viewport-fit=cover');
            const backgroundColor = parseInt(getMeta('theme-color')!.substring(1), 16);
            const appBarColor = parseInt(getMeta('app-bar-color')!.substring(1), 16);
            const navBarColor = parseInt(getMeta('nav-bar-color')!.substring(1), 16);

            window.App.updateAppStyle({
                backgroundColor,
                appBarColor,
                navBarColor,
                appBarOffset,
                navBarOffset,
                keyboardResize,
            });
        } catch (e) {
            // eslint-disable-next-line no-console
            console.error('Unable to update app style', e);
        }
    }, false);
}
const App: IApp = window.App;
export default App;
