import {routeInverted, timestamp, uuid} from "@/misc.js";
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
import {useOrderStore} from "@/store/store.js";
import {encodeIEE754} from "@/common.js";
import {defineStore} from "pinia";
import {ref} from "vue";


function unimplemented() { throw Error('Unimplemented') }

// Builders are data objects which store a configuration state
// the component name must match a corresponding Vue component in the BuilderFactory.vue component, which is responsible
// for instantiating the UI component for a given builder dictionary, based on its builder.component field.
export function newBuilder( component, options = {}) {
    const id = uuid()
    return {id, component, options, points: {}, shapes: {}, props: {}, build: unimplemented}
}

// Orders hold an amount and builders
// noinspection JSUnusedLocalSymbols
const Order = {
    id: uuid(),
    amount: 0,
    builders: [],
}


export const useChartOrderStore = defineStore('chart_orders', () => {
    const chartReady = ref(false)

    const orders = ref([])
    const selectedOrder = ref(null)
    const selectedSymbol = ref(null)
    const selectedPool = ref(null)

    const drawing = ref(false)
    const drawingCallbacks = ref(null)  // only during draw mode

    function newOrder() {
        console.log('cos new order')
        const order = {id:uuid(), amount:1, amountIsTokenA: true, buy: true, builders:[], }
        orders.value.push(order)
        selectedOrder.value = order
    }

    function removeOrder(order) {
        let index = orders.value.findIndex((o)=>o.id===order.id)
        if (index === -1) return
        orders.value = orders.value.filter((o)=>o.id!==order.id)
        if (orders.value.length === 0)
            selectedOrder.value = null
        else
            selectedOrder.value = orders.value[Math.max(0,index-1)] // select the order above the removed one
    }

    return {
        chartReady, selectedSymbol, selectedPool, orders, drawing, drawingCallbacks, newOrder, removeOrder,
    }
})


export function applyLimit(tranche, price = null, isMinimum = null) {
    if (price === null) {
        const os = useOrderStore()
        price = os.limitPrice
        if (!price)
            return
    }

    applyLine(tranche, price, 0, isMinimum)
}


function computeInterceptSlope(time0, price0, time1, price1) {
    if (!time0 || !price0 && price0 !== 0 || !time1 || !price1 && price1 !== 0)
        throw Error(`invalid line points data ${time0} ${price0} ${time1} ${price1}`)
    const t0 = time0
    const t1 = time1
    if (t0 === t1)
        throw Error("line points' times must be different")
    const slope = (price1 - price0) / (t1 - t0)
    const intercept = price1 - slope * t1
    return [intercept, slope]
}


export function linePointsValue(time0, price0, time1, price1, unixTime = null) {
    if (unixTime === null)
        unixTime = timestamp()
    try {
        const [intercept, slope] = computeInterceptSlope(time0, price0, time1, price1)
        return intercept + unixTime * slope
    } catch (e) {
        console.log('error computing line', e)
        return null
    }
}


export function applyLinePoints(tranche, time0, price0, time1, price1, isMinimum = null) {
    const [intercept, slope] = computeInterceptSlope(time0, price0, time1, price1);
    applyLine(tranche, intercept, slope, isMinimum)
}


export function applyLine(tranche, intercept, slope, isMinimum = null) {
    console.log('intercept, slope', intercept, slope)
    // intercept and slope are still in "human" units of decimal-adjusted prices
    const os = useOrderStore()
    const route = os.route
    const inverted = routeInverted(route)
    const scale = 10 ** (os.tokenA.d - os.tokenB.d)
    let m = inverted ? -scale / slope : slope / scale
    let b = inverted ? scale / intercept : intercept / scale
    const cur = b + timestamp() * m
    console.log('inverted b, m, cur', inverted, b, m, cur)
    m = encodeIEE754(m)
    b = encodeIEE754(b)
    if (isMinimum === null)
        isMinimum = os.limitIsMinimum
    console.log('limit is minimum', isMinimum)
    if (isMinimum) {
        tranche.minIntercept = b;
        tranche.minSlope = m;
    } else {
        tranche.maxIntercept = b;
        tranche.maxSlope = m;
    }
    tranche.marketOrder = false;
}

export function timesliceTranches() {
    const ts = []
    const os = useOrderStore()
    const n = os.tranches // num tranches
    const interval = os.interval;
    const timeUnitIndex = os.timeUnitIndex;
    let duration =
        timeUnitIndex === 0 ? interval * 60 :      // minutes
            timeUnitIndex === 1 ? interval * 60 * 60 : // hours
                interval * 24 * 60 * 60;                   // days
    let window
    if (!os.intervalIsTotal) {
        window = duration
        duration *= n // duration is the total time for all tranches
    } else {
        window = Math.round(duration / n)
    }
    if (window < 60)
        window = 60  // always allow at least one minute for execution
    const amtPerTranche = Math.ceil(MAX_FRACTION / n)
    duration -= 15  // subtract 15 seconds so the last tranche completes before the deadline
    for (let i = 0; i < n; i++) {
        const start = Math.floor(i * (duration / Math.max((n - 1), 1)))
        const end = start + window
        ts.push(newTranche({
            fraction: amtPerTranche,
            startTimeIsRelative: true,
            startTime: start,
            endTimeIsRelative: true,
            endTime: end,
        }))
    }
    return ts
}

