import {FixedNumber} from "ethers";
import {usePrefStore, useStore} from "@/store/store.js";
import {token} from "@/blockchain/token.js";
import Color from "color";

export class SingletonCoroutine {
    constructor(f, delay = 10) {
        this.f = f
        this.delay = delay
        this.timeout = null
        this.args = null
    }

    pending() {
        return this.timeout !== null
    }

    invoke(/*arguments*/) {
        this.args = arguments
        // console.log('invoke', arguments)
        if (this.timeout === null)
            // noinspection JSCheckFunctionSignatures
            this.timeout = setTimeout(this.onTimeout, this.delay, this)
    }

    async onTimeout(self) {
        try {
            await self.f(...self.args)
        } catch (e) {
            if (self.retry) {
                console.log('retrying', this.f, 'due to error', e)
                self.timeout = null
                self.invoke(self.args)
            } else
                console.error(e)
        } finally {
            self.timeout = null
        }
    }

}

export const vAutoSelect = {
    beforeMount: (el) => {
        const input = el.querySelector('input')
        input.onfocus = () => setTimeout(() => input.select(), 0)
    }
}
export const uint32max = 4294967295n
export const uint64max = 18446744073709551615n

export function tokenNumber(token, balance) {
    return FixedNumber.fromValue(balance, token.decimals, {decimals: token.decimals, width: 256})
}

export function tokenFloat(token, balance) {
    return tokenNumber(token, balance).toUnsafeFloat()
}

export function routeInverted(route) {
    const s = useStore()
    return route && (route.token0 === s.tokenA) === s.inverted
}

export function intervalString(seconds) {
    if (seconds < 1)
        return 'now'
    else if (seconds < 60)
        return `${seconds} seconds`
    else if (seconds < 3600)
        return `${(seconds / 60).toFixed(1)} minutes`
    else if (seconds < 86400)
        return `${(seconds / 3600).toFixed(1)} hours`
    else
        return `${(seconds / 86400).toFixed(1)} days`
}

const _dateFormat = new Intl.DateTimeFormat(undefined, {dateStyle: 'medium', timeStyle: 'short'})

export function dateString(seconds) {
    const date = new Date(seconds * 1000)
    return _dateFormat.format(date)
}


export function timestamp(date = null) {
    if (date === null)
        date = new Date()
    return Math.round(date.getTime() / 1000)
}


export function pairKey(tokenA, tokenB) {
    const token0 = tokenA.a < tokenB.a ? tokenA.a : tokenB.a
    const token1 = tokenA.a > tokenB.a ? tokenA.a : tokenB.a
    return [token0, token1];
}


export function pairPriceAddr(baseTokenAddr, quoteTokenAddr, price) {
    const baseToken = token(baseTokenAddr)
    const quoteToken = token(quoteTokenAddr)
    if (!baseToken || !quoteToken)
        return null
    return pairPrice(baseToken, quoteToken, price)
}


export function pairPrice(baseToken, quoteToken, price, decimals = null) {
    if (price === null || price === undefined)
        return price
    if (decimals === null)
        decimals = baseToken.decimals - quoteToken.decimals
    console.log('pairPrice', baseToken, quoteToken, price, decimals)
    if (decimals >= 0)
        price /= 10 ** decimals
    else
        price *= 10 ** -decimals
    const invertedKey = pairKey(baseToken, quoteToken);
    const prefs = usePrefStore()
    if (!(invertedKey in prefs.inverted)) {
        // todo prefer stablecoins as the quote asset
        prefs.inverted[invertedKey] = false
    }
    if (prefs.inverted[invertedKey])
        price = 1 / price
    return price
}

export const sleep = ms => new Promise(r => setTimeout(r, ms))

export function builderDefaults(props, emit, defaults) {
    let changed = false
    for (const k in defaults)
        if (props.builder[k] === undefined) {
            props.builder[k] = defaults[k] instanceof Function ? defaults[k]() : defaults[k]
            changed = true
        }
    if (changed)
        emit('update:builder', props.builder)
}

export function uuid() {
    return crypto.randomUUID();
}

export function lightenColor(color, lightness = 85, alpha = null) {
    let c = new Color(color).hsl().lightness(lightness)
    if (alpha !== null)
        c = c.rgb().alpha(alpha)
    return c.string()
}

export function lightenColor2(color, alpha = null) {
    return lightenColor(color, 98, alpha)
}


const colorRanges = {
    buy: ['#00CC33', '#3300CC'],
    sell: ['#CC0033', '#CCCC33'],
}

export function lineColor(buy, index) {
    const range = buy ? colorRanges.buy : colorRanges.sell
    const a = new Color(range[0]).rgb()
    const b = new Color(range[1]).rgb()
    // const z2 = Math.PI * Math.PI / 6 - 1 // ζ(2) modulo 1
    const z2 = 1 - 1/Math.E // ζ(2) modulo 1
    const kk = index * z2;
    const k = kk - Math.trunc(kk)
    const mix = (x, y) => (1 - k) * x + k * y
    const c = new Color([mix(a.red(), b.red()), mix(a.green(), b.green()), mix(a.blue(), b.blue())])
    // console.log('default color', c.string())
    return c.string()
}

