import { setTitle } from '../../functions/Route'
import HeaderMessage from '../layout/HeaderMessage'
import InputButton from '../inputs/Button'
import InputButtonRow from '../inputs/ButtonRow'
import InputLabel from '../inputs/Label'
import InputDescription from '../inputs/Description'
import './styles/Settings.css'

export default function ViewSettings() {
    const GIFUU_PREFIX = '_|GIFUU_BACKUP:DO_NOT_SHARE|_'

    async function onDataBackup() {
        const password = prompt('Please enter a password for this backup (Leave empty for none):')
        if (password === null) return

        // Generate Key
        const enc = new TextEncoder()
        const keyMaterial = await crypto.subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, ['deriveKey'])
        const salt = crypto.getRandomValues(new Uint8Array(16))
        const key = await crypto.subtle.deriveKey(
            { name: 'PBKDF2', salt, iterations: 100_000, hash: 'SHA-256' },
            keyMaterial,
            { name: 'AES-GCM', length: 256 },
            false,
            ['encrypt'],
        )

        // Encrypt String
        const data = JSON.stringify(localStorage)
        const nonce = crypto.getRandomValues(new Uint8Array(12))
        const cipher = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: nonce }, key, enc.encode(data))

        // Package Contents
        const bundle = new Uint8Array(salt.byteLength + nonce.byteLength + cipher.byteLength)
        bundle.set(salt, 0)
        bundle.set(nonce, 16)
        bundle.set(new Uint8Array(cipher), 28)

        const string = GIFUU_PREFIX + btoa(String.fromCharCode(...bundle))
        navigator.clipboard.writeText(string)

        alert('Data has been copied to clipboard.')
    }

    async function onDataRestore() {
        // Retrieve User Data
        const blob = await navigator.clipboard.readText()
        if (!blob.startsWith(GIFUU_PREFIX)) {
            alert('Contents stored in clipboard is not a backup.')
            return
        }
        const password = prompt('Please enter the password for this backup:')
        if (password === null) return

        // Unpackage Contents
        const bundle = Uint8Array.from(atob(blob.slice(GIFUU_PREFIX.length)), (c) => c.charCodeAt(0))
        const salt = bundle.slice(0, 16)
        const iv = bundle.slice(16, 28)
        const cipher = bundle.slice(28)

        // Generate Key
        const enc = new TextEncoder()
        const keyMaterial = await crypto.subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, ['deriveKey'])
        const key = await crypto.subtle.deriveKey(
            { name: 'PBKDF2', salt, iterations: 100_000, hash: 'SHA-256' },
            keyMaterial,
            { name: 'AES-GCM', length: 256 },
            false,
            ['decrypt'],
        )

        // Decrypt String
        let raw: string
        try {
            const plain = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, cipher)
            raw = new TextDecoder().decode(plain)
        } catch (err) {
            console.error('Backup Error:', err)
            alert('Incorrect Password')
            return
        }

        // Parse String
        try {
            const data = JSON.parse(raw)
            if (typeof data !== 'object' || Array.isArray(data)) {
                throw 'Invalid Object'
            }
            Object.entries(data).forEach(([k, v]) => {
                if (typeof k === 'string' && typeof v === 'string') {
                    localStorage.setItem(k, v)
                }
            })
        } catch (err) {
            console.error('Backup Error:', err)
            alert('Invalid data found in backup.')
        }

        alert('Data restored, the webpage will now refresh.')
        window.location.reload()
    }

    setTitle('Settings')
    return (
        <>
            <HeaderMessage label="View: Settings" />
            <div className="view-settings">
                <div className="section">
                    <InputLabel for="" label="Data Management" />
                    <InputDescription>
                        Export your local preferences and tokens for transfer or backup purposes to your devices
                        clipboard. Do not share these with anybody!
                    </InputDescription>
                    <InputButtonRow split={true}>
                        <InputButton
                            id="action-backup"
                            label="Backup"
                            onClick={onDataBackup}
                            disabled={false}
                            rainbow={false}
                            selected={false}
                        />
                        <InputButton
                            id="action-restore"
                            label="Restore"
                            onClick={onDataRestore}
                            disabled={false}
                            rainbow={false}
                            selected={false}
                        />
                    </InputButtonRow>
                </div>
            </div>
        </>
    )
}
