/**
 * E2E Encryption Service using TweetNaCl
 * https://tweetnacl.js.org/
 */

const STORAGE_KEY = 'dgray_keypair'

class CryptoService {
    constructor() {
        this.nacl = null
        this.naclUtil = null
        this.keyPair = null
        this.ready = this.init()
    }

    async init() {
        if (!window.nacl) {
            await this.loadScript('/js/vendor/nacl-fast.min.js')
            await this.loadScript('/js/vendor/nacl-util.min.js')
        }
        
        this.nacl = window.nacl
        this.naclUtil = window.nacl.util
        
        this.loadOrCreateKeyPair()
    }

    loadScript(src) {
        return new Promise((resolve, reject) => {
            if (document.querySelector(`script[src="${src}"]`)) {
                resolve()
                return
            }
            const script = document.createElement('script')
            script.src = src
            script.onload = resolve
            script.onerror = reject
            document.head.appendChild(script)
        })
    }

    loadOrCreateKeyPair() {
        const stored = localStorage.getItem(STORAGE_KEY)
        
        if (stored) {
            try {
                const parsed = JSON.parse(stored)
                this.keyPair = {
                    publicKey: this.naclUtil.decodeBase64(parsed.publicKey),
                    secretKey: this.naclUtil.decodeBase64(parsed.secretKey)
                }
                return
            } catch (e) {
                console.warn('Failed to load keypair, generating new one')
            }
        }
        
        this.generateKeyPair()
    }

    generateKeyPair() {
        this.keyPair = this.nacl.box.keyPair()
        
        const toStore = {
            publicKey: this.naclUtil.encodeBase64(this.keyPair.publicKey),
            secretKey: this.naclUtil.encodeBase64(this.keyPair.secretKey)
        }
        
        localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore))
    }

    getPublicKey() {
        if (!this.keyPair) return null
        return this.naclUtil.encodeBase64(this.keyPair.publicKey)
    }

    /**
     * Encrypt a message for a recipient
     * @param {string} message - Plain text message
     * @param {string} recipientPublicKey - Base64 encoded public key
     * @returns {object} - { nonce, ciphertext } both base64 encoded
     */
    encrypt(message, recipientPublicKey) {
        const nonce = this.nacl.randomBytes(this.nacl.box.nonceLength)
        const messageUint8 = this.naclUtil.decodeUTF8(message)
        const recipientKey = this.naclUtil.decodeBase64(recipientPublicKey)
        const sharedKey = this.nacl.box.before(recipientKey, this.keyPair.secretKey)
        
        const encrypted = this.nacl.secretbox(messageUint8, nonce, sharedKey)
        
        return {
            nonce: this.naclUtil.encodeBase64(nonce),
            ciphertext: this.naclUtil.encodeBase64(encrypted)
        }
    }

    /**
     * Decrypt a message from a sender
     * @param {string} ciphertext - Base64 encoded ciphertext
     * @param {string} nonce - Base64 encoded nonce
     * @param {string} otherPublicKey - Base64 encoded public key of the other party
     * @returns {string|null} - Decrypted message or null if failed
     */
    decrypt(ciphertext, nonce, otherPublicKey) {
        try {
            const otherKey = this.naclUtil.decodeBase64(otherPublicKey)
            const sharedKey = this.nacl.box.before(otherKey, this.keyPair.secretKey)
            
            const decrypted = this.nacl.secretbox.open(
                this.naclUtil.decodeBase64(ciphertext),
                this.naclUtil.decodeBase64(nonce),
                sharedKey
            )
            
            if (!decrypted) return null
            return this.naclUtil.encodeUTF8(decrypted)
        } catch (e) {
            console.error('Decryption failed:', e)
            return null
        }
    }
}

export const cryptoService = new CryptoService()
