import Alpine from 'alpinejs';
import {
    clearData,
    deleteProfile,
    getProfileIndex,
    getProfileNames,
    getRelays,
    initialize,
    newProfile,
    savePrivateKey,
    saveProfileName,
    saveRelays,
    RECOMMENDED_RELAYS,
    getPermissions,
    setPermission,
    KINDS,
    humanPermission,
    validateKey,
} from './utilities/utils';

const log = console.log;

function go(url) {
    browser.tabs.update({ url: browser.runtime.getURL(url) });
}

Alpine.data('options', () => ({
    profileNames: ['---'],
    profileIndex: 0,
    profileName: '',
    pristineProfileName: '',
    privKey: '',
    pristinePrivKey: '',
    pubKey: '',
    relays: [],
    newRelay: '',
    urlError: '',
    recommendedRelay: '',
    permissions: {},
    host: '',
    permHosts: [],
    hostPerms: [],
    visible: false,
    copied: false,
    setPermission,
    go,

    async init(watch = true) {
        log('Initialize backend.');
        await initialize();

        if (watch) {
            this.$watch('profileIndex', async () => {
                await this.refreshProfile();
                this.host = '';
            });

            this.$watch('host', () => {
                this.calcHostPerms();
            });

            this.$watch('recommendedRelay', async () => {
                if (this.recommendedRelay.length == 0) return;
                await this.addRelay(this.recommendedRelay);
                this.recommendedRelay = '';
            });
        }

        // We need to refresh the names BEFORE setting the profile index, or it won't work
        // on init to set the correct profile.
        await this.getProfileNames();
        await this.getProfileIndex();
        this.setProfileIndexFromSearch();
        await this.refreshProfile();
    },

    async refreshProfile() {
        await this.getProfileNames();
        await this.getProfileName();
        await this.getNsec();
        await this.getNpub();
        await this.getRelays();
        await this.getPermissions();
    },

    // Profile functions

    setProfileIndexFromSearch() {
        let p = new URLSearchParams(window.location.search);
        let index = p.get('index');
        if (!index) {
            return;
        }
        this.profileIndex = parseInt(index);
    },

    async getProfileNames() {
        this.profileNames = await getProfileNames();
    },

    async getProfileName() {
        let names = await getProfileNames();
        let name = names[this.profileIndex];
        this.profileName = name;
        this.pristineProfileName = name;
    },

    async getProfileIndex() {
        this.profileIndex = await getProfileIndex();
    },

    async newProfile() {
        let newIndex = await newProfile();
        await this.getProfileNames();
        this.profileIndex = newIndex;
    },

    async deleteProfile() {
        if (
            confirm(
                'This will delete this profile and all associated data. Are you sure you wish to continue?'
            )
        ) {
            await deleteProfile(this.profileIndex);
            await this.init(false);
        }
    },

    async copyPubKey() {
        await navigator.clipboard.writeText(this.pubKey);
        this.copied = true;
        setTimeout(() => {
            this.copied = false;
        }, 1500);
    },

    // Key functions

    async saveProfile() {
        if (!this.needsSave) return;

        console.log('saving private key');
        await savePrivateKey(this.profileIndex, this.privKey);
        console.log('saving profile name');
        await saveProfileName(this.profileIndex, this.profileName);
        console.log('getting profile name');
        await this.getProfileNames();
        console.log('refreshing profile');
        await this.refreshProfile();
    },

    async getNpub() {
        this.pubKey = await browser.runtime.sendMessage({
            kind: 'getNpub',
            payload: this.profileIndex,
        });
    },

    async getNsec() {
        this.privKey = await browser.runtime.sendMessage({
            kind: 'getNsec',
            payload: this.profileIndex,
        });
        this.pristinePrivKey = this.privKey;
    },

    // Relay functions

    async getRelays() {
        this.relays = await getRelays(this.profileIndex);
    },

    async saveRelays() {
        await saveRelays(this.profileIndex, this.relays);
        await this.getRelays();
    },

    async addRelay(relayToAdd = null) {
        let newRelay = relayToAdd || this.newRelay;
        try {
            let url = new URL(newRelay);
            if (url.protocol !== 'wss:') {
                this.setUrlError('Must be a websocket url');
                return;
            }
            let urls = this.relays.map(v => v.url);
            if (urls.includes(url.href)) {
                this.setUrlError('URL already exists');
                return;
            }
            this.relays.push({ url: url.href, read: true, write: true });
            await this.saveRelays();
            this.newRelay = '';
        } catch (error) {
            this.setUrlError('Invalid websocket URL');
        }
    },

    async deleteRelay(index) {
        this.relays.splice(index, 1);
        await this.saveRelays();
    },

    setUrlError(message) {
        this.urlError = message;
        setTimeout(() => {
            this.urlError = '';
        }, 3000);
    },

    // Permissions

    async getPermissions() {
        this.permissions = await getPermissions(this.profileIndex);

        // Set the convenience variables
        this.calcPermHosts();
        this.calcHostPerms();
    },

    calcPermHosts() {
        let hosts = Object.keys(this.permissions);
        hosts.sort();
        this.permHosts = hosts;
    },

    calcHostPerms() {
        let hp = this.permissions[this.host] || {};
        let keys = Object.keys(hp);
        keys.sort();
        this.hostPerms = keys.map(k => [k, humanPermission(k), hp[k]]);
        console.log(this.hostPerms);
    },

    permTypes(hostPerms) {
        let k = Object.keys(hostPerms);
        k = Object.keys.sort();
        k = k.map(p => {
            let e = [p, hostPerms[p]];
            if (p.startsWith('signEvent')) {
                let n = parseInt(p.split(':')[1]);
                let name =
                    KINDS.find(kind => kind[0] === n) || `Unknown (Kind ${n})`;
                e = [name, hostPerms[p]];
            }
            return e;
        });
        return k;
    },

    // General

    async clearData() {
        if (
            confirm(
                'This will remove your private keys and all associated data. Are you sure you wish to continue?'
            )
        ) {
            await clearData();
            await this.init(false);
        }
    },

    async closeOptions() {
        const tab = await browser.tabs.getCurrent();
        await browser.tabs.remove(tab.id);
    },

    // Properties

    get recommendedRelays() {
        let relays = this.relays.map(r => new URL(r.url)).map(r => r.href);
        return RECOMMENDED_RELAYS.filter(r => !relays.includes(r.href)).map(
            r => r.href
        );
    },

    get hasRelays() {
        return this.relays.length > 0;
    },

    get hasRecommendedRelays() {
        return this.recommendedRelays.length > 0;
    },

    get needsSave() {
        return (
            this.privKey !== this.pristinePrivKey ||
            this.profileName !== this.pristineProfileName
        );
    },

    get validKey() {
        return validateKey(this.privKey);
    },

    get validKeyClass() {
        return this.validKey
            ? ''
            : 'ring-2 ring-rose-500 focus:ring-2 focus:ring-rose-500 border-transparent focus:border-transparent';
    },

    get visibilityClass() {
        return this.visible ? 'text' : 'password';
    },
}));

Alpine.start();
