import contracts from "../contracts"
import oracle from "../../artifacts/contracts/condition_oracles/OneTimeOffchainTickets.sol/OneTimeOffchainTickets.json"
import {initContractByAbi} from "../utils"
import {BigNumber, utils, Wallet} from "ethers"

const ONE_TIME_TICKET_CONTRACT = 'one_time_offchain_tickets'

const abi = () => {
  return oracle.abi
}

const bytecode = () => {
  return oracle.bytecode
}

const deployedAddress = (network : string | number) => {
  if (!contracts[`${network}`]) return null
  return contracts[`${network}`][ONE_TIME_TICKET_CONTRACT]
}

const initContract = (contractKey : string, readOnly = true, web3Provider = null) => {
  return initContractByAbi(oracle.abi, contractKey, readOnly, web3Provider)
}

const owner = (web3Provider = null, contractKey = ONE_TIME_TICKET_CONTRACT) => {
  return initContract(contractKey, true, web3Provider)
    .then(contract => contract.owner())
}

const getDomainSeparator = (web3Provider = null, contractKey = ONE_TIME_TICKET_CONTRACT) => {
  return initContract(contractKey, true, web3Provider)
    .then(contract => contract.getDomainSeparator())
}

const nextNonce = (account : string, web3Provider = null, contractKey = ONE_TIME_TICKET_CONTRACT) => {
  return initContract(contractKey, true, web3Provider)
    .then(contract => contract.nextNonce(account))
}

const claimedAmount = (account : string, web3Provider = null, contractKey = ONE_TIME_TICKET_CONTRACT) => {
  return initContract(contractKey, true, web3Provider)
    .then(contract => contract.claimedAmount(account))
}

const isAllowed = (
  user : string,
  amount : number | BigNumber,
  claimedAmount : number | BigNumber,
  nonce : number, 
  callData : string,
  web3Provider = null,
  contractKey = ONE_TIME_TICKET_CONTRACT
) => {
  return initContract(contractKey, true, web3Provider)
    .then(contract => contract.isAllowed(user, amount, claimedAmount, nonce, callData))
}

const hasClaim = (account : string, claim : string, web3Provider = null, contractKey = ONE_TIME_TICKET_CONTRACT) => {
  return initContract(contractKey, true, web3Provider)
    .then(contract => contract.hasClaim(account, claim))
}

const prepareOffchainClaim = (
  claimInterface : string,
  rewardToken : string,
  rewardTokenId : number | BigNumber,
  user : string,
  amount : number | BigNumber,
  claimedAmount : number | BigNumber,
  nonce : number,
  ticketSignature : string
) => {
  const integrityHash = utils.solidityKeccak256(["address", "uint256", "bytes4"], [rewardToken, rewardTokenId, claimInterface])
  const ticketData = utils.defaultAbiCoder.encode(
    ["address", "uint256", "uint256", "uint32", "bytes"],
    [user, amount, claimedAmount, nonce, ticketSignature]
  )
  return utils.defaultAbiCoder.encode(["bytes32", "bytes"], [integrityHash, ticketData])
}

const signTicket = async function(
  signer : Wallet,
  domainSeparator : string,
  user : string,
  amount : number | BigNumber,
  claimedAmount : number | BigNumber,
  nonce : number
) {
  const ticketMessage = utils.keccak256(
      utils.defaultAbiCoder.encode(
          ["bytes32", "address", "uint256", "uint256", "uint32"], [
              domainSeparator,
              user,
              amount,
              claimedAmount,
              nonce
          ],
      ),
  )

  const ticketSignature = await signer.signMessage(utils.arrayify(ticketMessage))
  const sig = utils.splitSignature(
      utils.arrayify(ticketSignature)
  );
  const ticketSignatureEncoded = utils.defaultAbiCoder.encode(
      ["uint8", "bytes32", "bytes32"], [sig.v, sig.r, sig.s],
  )
  return ticketSignatureEncoded
}

export {
  abi,
  bytecode,
  deployedAddress,
  owner,
  getDomainSeparator,
  nextNonce,
  claimedAmount,
  isAllowed,
  hasClaim,
  prepareOffchainClaim,
  signTicket
}
export default {
  abi,
  bytecode,
  deployedAddress,
  owner,
  getDomainSeparator,
  nextNonce,
  claimedAmount,
  isAllowed,
  hasClaim,
  prepareOffchainClaim,
  signTicket
}