import { default as CryptoJs } from 'crypto-js'
import { ethers } from 'ethers'
import { CONTRACT_INTERFACES } from '../..'

export { CryptoJs }

export const randomKeyWithLength = (length: number): string =>
	CryptoJs.lib.WordArray.random(length).toString()

export const id = (): string => {
	return randomKeyWithLength(24)
}

export const uInt8ArrayFromHexString = (hexString: string): Uint8Array =>
	Uint8Array.from(
		hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)),
	)

export const uInt8ArrayFromString = (str: string): Uint8Array => {
	let utf8Encode = new TextEncoder()
	return utf8Encode.encode(str)
}

const hashString = (valueStr: string) => {
	return ethers.utils.keccak256(ethers.utils.toUtf8Bytes(valueStr))
}

export const getHashedMessage = (
	originalMessage: string,
): CONTRACT_INTERFACES.IHashedMessage => {
	const hashedMessage = hashString(originalMessage)
	return {
		originalMessage,
		hashedMessage,
	}
}

export const splitSignature = (
	signature: string,
): {
	r: string
	s: string
	v: number
} => {
	const r = signature.slice(0, 66)
	const s = '0x' + signature.slice(66, 130)
	const v = parseInt(signature.slice(130, 132), 16)
	return { r, s, v }
}

export const getSignedMessage = async (
	hashedMessage: CONTRACT_INTERFACES.IHashedMessage,
	signer: ethers.Signer,
): Promise<CONTRACT_INTERFACES.IHashedSignedMessage> => {
	const signature = await signer.signMessage(hashedMessage.hashedMessage)
	return {
		...hashedMessage,
		signature,
		...splitSignature(signature),
	}
}

export const getHashedSignedMessage = (
	originalMessage: string,
	signer: ethers.Signer,
): Promise<CONTRACT_INTERFACES.IHashedSignedMessage> => {
	return getSignedMessage(getHashedMessage(originalMessage), signer)
}

export const validateHashedSignedMessage = async (
	hashedSignedMessage: CONTRACT_INTERFACES.IHashedSignedMessage,
	expectedAddress?: string,
): Promise<string> => {
	const probeHashed = getHashedMessage(hashedSignedMessage.originalMessage)
	if (probeHashed.originalMessage !== hashedSignedMessage.originalMessage) {
		throw new Error('The hashed message does not match original message')
	}
	const signerAddr = await ethers.utils.verifyMessage(
		hashedSignedMessage.hashedMessage,
		hashedSignedMessage.signature,
	)
	if (expectedAddress !== undefined) {
		if (signerAddr !== expectedAddress) {
			throw new Error(
				`Signature address mismatch. Expected ${expectedAddress}, got ${signerAddr}`,
			)
		}
	}

	return signerAddr
}
