// Utilities
import {defineStore} from 'pinia'
import {knownTokens} from "@/knownTokens.js";
import {computed, ref} from "vue";
import {version as versionMeta} from "@/version.js";

// USING THE STORE:
//
//
// When defining the store, use Vue script setup syntax, which requires assignment to .value:
//
// defineStore('foostore', ()=>{
//   const foo = ref(true)
//   const obj = ref({})
//   function reset() {
//     obj.value = {}
//     foo.value = false
//   }
// })
//
//
// Then use the store in components:
//
// const s = useStore()
// if( s.foo )      // store variables may be read simply
//   s.reset()      // store actions (functions) may be called directly
// const stuff = [ {}, [], true ]
// if( s.obj.value in stuff || s.obj.value === {} ) ...  // use .value to access the raw object instead of its reference

function timestamp() {
  return Math.round(new Date().getTime() / 1000)
}

const UNKNOWN_PROVIDER = {}

const REQUIRE_APPROVAL = import.meta.env.VITE_REQUIRE_APPROVAL !== 'NO';

export const useStore = defineStore('app', ()=> {
  const clock = ref(timestamp())  // the clock ticks infrequently enough to be mostly stable for user display
  setInterval(()=>clock.value=timestamp(), 10*1000)  // 10 secs

  const nav = ref(false) // controls opening navigation drawer
  const theme = ref('dark')

  const connected = ref(true)  // we assume a connection will succeed until socket.io complains otherwise
  const regionApproved = ref(REQUIRE_APPROVAL?null:true)
  const walletApproved = ref(REQUIRE_APPROVAL?null:true)
  const approved = computed(()=>(regionApproved.value && walletApproved.value))

  const _chainId = ref(Number(Object.keys(versionMeta.chainInfo)[0]))
  const _chainInfo = ref(versionMeta.chainInfo)

  function getTokenList() {
    const chains = _chainId.value in _chainInfo.value && _chainInfo.value[_chainId.value].tokens !== undefined ?
        _chainInfo.value[_chainId.value].tokens : []
    let known = knownTokens[_chainId.value]
    known = known ? Object.values(known) : []
    let extras = extraTokens[_chainId.value]
    extras = extras ? Object.values(extras) : []
    return [...chains, ...known, ...extras]; // put chains first so the Mockcoin pool is automatically selected
  }
  function getTokens() {
    const result = {}
    const all = getTokenList();
    for (const token of all)
      result[token.address] = token
    return result
  }
  const chainId = computed({
    get() {return _chainId.value},
    set(v) {
      console.log('setting app chainid',_chainId.value, v)
      const changed = _chainId.value!==v;
      if (changed) {
        console.log('do set')
        _chainId.value = v
        account.value = null
      }
      if (changed || providerRef.value === null) {
        console.log('invalidating provider')
        providerRef.value = UNKNOWN_PROVIDER
      }
    }
  })
  const chainInfo = computed({
    get() {return _chainInfo.value},
    set(v) {_chainInfo.value=v}
  })
  const chain = computed(() => !_chainId.value ? null : (_chainInfo.value[_chainId.value] || null))
  const providerRef = ref(null)
  const provider = computed(()=>providerRef.value)
  const vaultInitCodeHash = computed(() => !chain.value ? null : chain.value.vaultInitCodeHash)
  const account = ref(null)
  const vaults = ref([])
  const vaultVersions = ref([])
  const transactionSenders = ref([]) // a list of function(signer) that send transactions
  const errors = ref([])
  const extraTokens = ref({})
  const poolPrices = ref({})  // keyed by [chainId,addr]
  const vaultBalances = ref({}) // indexed by vault addr then by token addr. value is an int
  const orders = ref({}) // indexed by vault value is another dictionary with orderIndex as key and order status values

  const vault = computed(() => vaults.value.length === 0 ? null : vaults.value[0] )
  const creatingVault = ref(false)  // used when a vault is first created but not yet responsive via metamask. If vault is not null, but creatingVault is true, then the vault is not yet ready for interaction.
  const upgrade = ref(null)
  const version = computed( () => vaultVersions.value.length === 0 ? 0 : vaultVersions.value[0] )
  const balances = computed( () => vault.value === null ? {} : vaultBalances.value[vault.value] || {} )
  const vaultOrders = computed(()=> vault.value === null || (!vault.value in orders.value) ? {} : orders.value[vault.value] ? orders.value[vault.value] : [] )
  const tokens = computed(getTokens)
  const markPrices = ref({})  // key: `${chainId}|${tokenAddr}` value: USD value
  function markPrice(token) { return markPrices.value[`${chainId.value}|${token}`] }
  const factory = computed(() => !chain.value ? null : chain.value.factory)
  const helper = computed(() => {console.log('chain helper', chain.value); return !chain.value ? null : chain.value.helper})
  const mockenv = computed(() => !chain.value ? null : chain.value.mockenv)
  const mockCoins = computed(() => !chain.value ? [] : !chain.value.mockCoins ? [] : chain.value.mockCoins)

  function removeTransactionSender(sender) {
    this.transactionSenders = this.transactionSenders.filter((v) => v !== sender)
  }
  function error(title, text, closeable=true) {
    this.errors.push({title, text, closeable})
  }
  function closeError(title, text) {
    const result = []
    this.errors.forEach((i)=>{if(i.title!==title && i.text!==text) result.push(i)})
    this.errors = result
  }
  function addToken(chainId, info) {
    let extras = this.extraTokens[chainId]
    if (extras === undefined) {
      extras = {}
      this.extraTokens[chainId] = extras
    }
    extras[info.address] = info
    this.extraTokens = extras
  }

  function getBalance(tokenAddr) {
    const found = this.balances[tokenAddr]
    return found === undefined ? 0 : found
  }

  return {
    connected,
    nav, chainId, chainInfo, chain, provider, providerRef, vaultInitCodeHash, account, vaults, vaultVersions,
    transactionSenders, errors, extraTokens, poolPrices, vaultBalances, orders, vault, version, upgrade, vaultOrders,
    tokens, factory, helper, theme,
    mockenv, mockCoins,
    removeTransactionSender, error, closeError, addToken, clock, balances,
    approved, regionApproved, walletApproved,
    getBalance, creatingVault,
    markPrices, markPrice,
  }
})


export const useOrderStore = defineStore('order', ()=> {
  const tokenA = ref(null)
  const tokenB = ref(null)

  // Order Input Forms
  // const tokenA = ref(null) // defined at top
  // const tokenB = ref(null)
  const buy = ref(true)
  const inverted = ref(false)
  const amount = ref(100) // todo adjust default
  const amountIsTokenA = ref(false) // todo adjust default
  const amountIsTotal = ref(true)
  const slippage = ref(0.10) // in percent units.  1 = 1%.  may be null.
  const limitPrice = ref(null)
  const limitPrice2 = ref(null)
  const tranches = ref(3)
  const interval = ref(1)
  const intervalIsTotal = ref(true)
  const timeUnitIndex = ref(0)
  const routes = ref([])
  const routesPending = ref(false)
  const utc = ref(false)

  const validOrder = computed(() => amount.value > 0 && routes.value.length > 0)
  const route = computed(() => routes.value.length === 0 ? null : routes.value[0])
  const base = computed(() => {
    const token = inverted.value ? tokenB.value : tokenA.value
    return !token ? {} : token
  })
  const quote = computed(() => {
    const token = inverted.value ? tokenA.value : tokenB.value
    return !token ? {} : token
  })
  const pairSymbol = computed(() => base.value?.s + '/' + quote.value?.s)
  const limitIsMinimum = computed(() => !(buy.value ^ inverted.value))
  const amountToken = computed(() => amountIsTokenA.value ? tokenA.value : tokenB.value)
  const amountIsInput = computed(() => amountIsTokenA.value !== buy.value)
  const totalAmount = computed(()=> amountIsTotal.value ? amount.value : amount.value * tranches.value )
  const trancheAmount = computed(()=> amountIsTotal.value ? amount.value / tranches.value : amount.value )

  function setDefaultTokens(tokens) {
    if( tokens.length > 0 )
      tokenA.value = tokens[0]
    if( tokens.length > 1 )
      tokenB.value = tokens[1]
  }

  return {
    tokenA, tokenB, buy, inverted, amount, amountIsTokenA, amountIsTotal, slippage, limitPrice, limitPrice2, tranches,
    interval, intervalIsTotal, timeUnitIndex, routes, routesPending, validOrder, route, base, quote, pairSymbol,
    limitIsMinimum, amountToken, amountIsInput, setDefaultTokens, totalAmount, trancheAmount, utc,
  }
})


export const usePrefStore = defineStore({
  id: 'pref',
  persist: true,
  state: ()=> {
    // user preferences
    const inverted = ref({})
    const hints = ref({})
    const newbie = ref(true)
    const acceptedTos = ref('NO TOS ACCEPTED')
    const selectedTicker = ref(null)
    const selectedTimeframe = ref(null)
    const timezone = ref('Etc/UTC')
    return {inverted, acceptedTos, selectedTicker, selectedTimeframe, timezone, newbie, hints, }
  },
})
