import {useStore} from "@/store/store.js";
import {Exchange} from "@/blockchain/orderlib.js";
import {uniswapV3PoolAddress} from "@/blockchain/uniswap.js";
import {FixedNumber} from "ethers";
import {newContract} from "@/blockchain/contract.js";
import {provider} from "@/blockchain/provider.js";
import {socket} from "@/socket.js";

const subscriptionCounts = {} // key is route and value is a subscription counter
export const WIDE_PRICE_FORMAT = {decimals:38, width:512, signed:false}; // 38 decimals is 127 bits


export function subPrices( routes ) {
    if( !routes.length )
        return
    const subRoutes = []
    let chainId = null
    for( const route of routes ) {
        // console.log('sub route', route, subscriptionCounts)
        const routeKey = [route.chainId, route.pool]
        if( !(routeKey in subscriptionCounts) || subscriptionCounts[routeKey] === 0 ) {
            subscriptionCounts[routeKey] = 1
            subRoutes.push(route)
        }
        else {
            subscriptionCounts[routeKey]++
        }
        if( chainId !== null && route.chainId !== chainId )
            throw Error('cannot mix chainIds in a subscription list')
        chainId = route.chainId
    }
    if( subRoutes.length ) {
        const pools = routes.map((r)=>r.pool);
        // console.log('subscribing to pools', pools)
        socket.emit('subPools', chainId, pools )
        // perform a local query if necessary
        const s = useStore()
        for( const route of subRoutes ) {
            const routeKey = [route.chainId, route.pool]
            if( !(routeKey in s.poolPrices) ) {
                getPriceForRoute(route).then((price)=>s.poolPrices[routeKey]=price)
            }
        }
    }
}

export function unsubPrices( routes ) {
    let chainId = null
    const unsubAddrs = []
    for( const route of routes ) {
        // console.log('unsub route', route, subscriptionCounts)
        const routeKey = [route.chainId, route.pool]
        if( !(routeKey in subscriptionCounts) ) {
            console.error('unsubscribed to a nonexistent route', route)
        }
        else {
            subscriptionCounts[routeKey]--
            if( subscriptionCounts[routeKey] === 0 ) {
                unsubAddrs.push(route.pool)
                if( chainId !== null && route.chainId !== chainId )
                    throw Error('cannot mix chainIds in a subscription list')
                // console.log('unsubscribing from pool', route.pool)
                chainId = route.chainId
            }
            else if( subscriptionCounts[routeKey] < 0 ) {
                console.error('unsubscribed to an already unsubbed route', route)
                subscriptionCounts[routeKey] = 0 // fix
            }
        }
    }
    if( unsubAddrs.length )
        socket.emit('unsubPools', chainId, unsubAddrs )
}


async function getPriceForRoute(route) {
    if( !route.token0 || !route.token1 )
        return null
    if( route.exchange === Exchange.UniswapV3 ) {
        const addr = uniswapV3PoolAddress(route.chainId, route.token0.address, route.token1.address, route.fee)
        const store = useStore();
        if( provider === null ) {
            console.error('provider was null during getPriceForRoute')
            return null
        }
        const pool = newContract(addr, 'IUniswapV3Pool', provider)
        const got = await pool.slot0()
        const [sqrtPrice,,,,,,] = got
        const spn = BigInt(sqrtPrice)
        let price = FixedNumber.fromValue(spn*spn, route.token1.decimals - route.token0.decimals, WIDE_PRICE_FORMAT)
        price = price.div(FixedNumber.fromValue(2n**(96n*2n),0,WIDE_PRICE_FORMAT))
        price = price.round(18).toString()
        // console.log(`price for ${route.token0.symbol}/${route.token1.symbol}`,price)
        store.poolPrices[[route.chainId,addr]] = price
        return price
    }
    else
        throw Error(`Unsupported exchange ${route.exchange}`)
}
