// Full BIP39 validation, including checksum and wordlist membership.
import wordlistTxt from '../bip39_wordlist.txt?raw';

// --- BIP39 Wordlist Loading ---
export const BIP39_WORDLIST: readonly string[] = wordlistTxt.trim().split(/\r?\n/);
export const WORD_INDEX = new Map<string, number>(
    BIP39_WORDLIST.map((word, index) => [word, index])
);

if (BIP39_WORDLIST.length !== 2048) {
    throw new Error(`Invalid wordlist loaded: expected 2048 words, got ${BIP39_WORDLIST.length}`);
}

// --- Web Crypto API Helpers ---
async function getCrypto(): Promise<SubtleCrypto> {
    if (globalThis.crypto?.subtle) {
        return globalThis.crypto.subtle;
    }
    try {
        const { webcrypto } = await import('crypto');
        if (webcrypto?.subtle) {
            return webcrypto.subtle as SubtleCrypto;
        }
    } catch (e) {
        // Ignore import errors
    }
    throw new Error("SubtleCrypto not found in this environment");
}

async function sha256(data: Uint8Array): Promise<Uint8Array> {
    const subtle = await getCrypto();
    // Create a new Uint8Array to ensure the underlying buffer is not shared.
    const dataCopy = new Uint8Array(data);
    const hashBuffer = await subtle.digest('SHA-256', dataCopy);
    return new Uint8Array(hashBuffer);
}

// --- Public API ---

export function normalizeBip39Mnemonic(words: string): string {
    return words.trim().toLowerCase().replace(/\s+/g, " ");
}

/**
 * Asynchronously validates a BIP39 mnemonic, including wordlist membership and checksum.
 * @param mnemonicStr The mnemonic string to validate.
 * @returns A promise that resolves to an object with a `valid` boolean and an optional `error` message.
 */
export async function validateBip39Mnemonic(mnemonicStr: string): Promise<{ valid: boolean; error?: string }> {
    const normalized = normalizeBip39Mnemonic(mnemonicStr);
    const words = normalized.length ? normalized.split(" ") : [];

    const validCounts = new Set([12, 15, 18, 21, 24]);
    if (!validCounts.has(words.length)) {
        return {
            valid: false,
            error: `Invalid word count: ${words.length}. Must be 12, 15, 18, 21, or 24.`,
        };
    }

    // 1. Check if all words are in the wordlist
    for (const word of words) {
        if (!WORD_INDEX.has(word)) {
            return {
                valid: false,
                error: `Invalid word: "${word}" is not in the BIP39 wordlist.`,
            };
        }
    }

    // 2. Reconstruct entropy and validate checksum
    try {
        let fullInt = 0n;
        for (const word of words) {
            fullInt = (fullInt << 11n) | BigInt(WORD_INDEX.get(word)!);
        }

        const totalBits = words.length * 11;
        const checksumBits = totalBits / 33;
        const entropyBits = totalBits - checksumBits;

        let entropyInt = fullInt >> BigInt(checksumBits);
        const entropyBytes = new Uint8Array(entropyBits / 8);

        for (let i = entropyBytes.length - 1; i >= 0; i--) {
            entropyBytes[i] = Number(entropyInt & 0xFFn);
            entropyInt >>= 8n;
        }

        const hashBytes = await sha256(entropyBytes);
        const computedChecksum = hashBytes[0] >> (8 - checksumBits);
        const originalChecksum = Number(fullInt & ((1n << BigInt(checksumBits)) - 1n));

        if (originalChecksum !== computedChecksum) {
            return {
                valid: false,
                error: "Invalid mnemonic: Checksum mismatch.",
            };
        }

    } catch (e) {
        return {
            valid: false,
            error: `An unexpected error occurred during validation: ${e instanceof Error ? e.message : 'Unknown error'}`,
        };
    }

    return { valid: true };
}
