import {DISTANT_FUTURE, DISTANT_PAST, MAX_FRACTION} from "@/blockchain/orderlib.js";
import {allocationText, DLine, HLine} from "@/charts/shape.js";
import {createShape, deleteShapeId, widget} from "@/charts/chart.js";
import {sideColor} from "@/misc.js";
import {useChartOrderStore} from "@/orderbuild.js";
import {timestamp} from "@/common.js";

export class OrderShapes {
    constructor(symbol, orderStatus) {
        this.symbol = symbol
        this.status = orderStatus
        this.trancheShapes = [];
        this.update(orderStatus)
    }

    update(orderStatus) {
        for (const old of this.trancheShapes)
            old.delete()
        this.status = orderStatus
        this.trancheShapes = []
        const maxAllocation = Math.max(...orderStatus.order.tranches.map((ts)=>ts.fraction)) / MAX_FRACTION
        if (orderStatus.trancheStatus !== undefined)
            for (let i = 0; i < orderStatus.trancheStatus.length; i++)
                this.trancheShapes.push(new TrancheShapes(this.symbol, this.status, i, maxAllocation));
    }

    show() {for (const t of this.trancheShapes) t.show();}
    hide() {for (const t of this.trancheShapes) t.hide();}
    delete() {for (const t of this.trancheShapes) t.delete(); this.shapes=[]}
}


class TrancheShapes {
    constructor(symbol, orderStatus, trancheIndex, maxAllocation) {
        if (symbol.inverted !== orderStatus.order.inverted) {
            console.log('OrderShapes.createLine(): symbol has wrong inverson for this order')
            return
        }
        this.symbol = symbol
        this.status = orderStatus
        this.trancheIndex = trancheIndex
        this.tranche = orderStatus.order.tranches[trancheIndex]
        this.trancheStatus = orderStatus.trancheStatus[trancheIndex]
        this.shapes = []
        this.fills = []
        this.maxAllocation = maxAllocation
        this.createShapes()
    }

    createShapes() {
        const t = this.tranche

        // console.log('create tranche shapes', t)
        if (t.marketOrder) {
            if (t.startTime !== DISTANT_PAST) {
                // todo vertical line
            }
        } else {
            // check lines
            const amount = this.status.order.amount * BigInt(t.fraction) / BigInt(MAX_FRACTION)
            const buy = this.status.order.tokenIn === this.symbol.quote.a
            this.createLine(t.minLine.slope, t.minLine.intercept, amount, buy);
            this.createLine(t.maxLine.slope, t.maxLine.intercept, amount, !buy);
        }
        for (const f of this.status.trancheStatus[this.trancheIndex].fills)
            this.createFillPoint(f)
    }

    createFillPoint(f) {
        // console.log('draw fill', f, this.symbol)
        const order = this.status.order;
        const symbol = this.symbol
        const scale = 10**symbol.decimals;
        const buy = order.tokenIn === this.symbol.quote.a
        const amountIsBase = buy !== this.status.order.amountIsInput
        const filledAmount = this.status.order.amountIsInput ? f.filledIn : f.filledOut;
        const amount = Number(filledAmount)
            * 10 ** -(amountIsBase ? this.symbol.base.d : this.symbol.quote.d)
        const weight = Number(filledAmount) / Number(this.status.order.amount)
        const amountSymbol = amountIsBase ? this.symbol.base.s : this.symbol.quote.s
        // console.log('fillpoint', buy, filledAmount, this.status.order, this.symbol)
        const time = f.time
        const out = Number(f.filledOut) / (1-this.status.order.route.fee/1000000)
        let price = out / Number(f.filledIn)
        if (buy)
            price = 1/price
        price *= scale
        // console.log('price', price)
        const channel = buy?'low':'high';
        const text = allocationText(buy, weight, amount, amountSymbol, amountIsBase ? null : this.symbol.base.s, 1, '\n')
        const color = sideColor(buy);
        const options = {channel,text,lock:true,overrides:{color: color, arrowColor: color}};
        const s = createShape(buy?'arrow_up':'arrow_down', {time, price}, options)
        console.log('created fill shape at', time, price, widget.activeChart().getShapeById(s).getProperties())
        this.fills.push(s)
    }

    createLine(slope, intercept, amountBigInt, breakout) {
        if (intercept === 0 && slope === 0) return
        const t = this.tranche
        const status = this.status
        const symbol = this.symbol
        const scale = 10**symbol.decimals;
        const buy = status.order.tokenIn === this.symbol.quote.a
        const decimals = buy ? this.symbol.base.d : this.symbol.quote.d;
        amountBigInt = BigInt(amountBigInt)
        const amount = Number(amountBigInt) * 10 ** - decimals
        const amountSymbol = buy ? this.symbol.base.s : this.symbol.quote.s
        const color = sideColor(buy)
        const maxAllocation = this.maxAllocation
        const allocation = t.fraction / MAX_FRACTION
        const ts = this.trancheStatus
        let sum = 0n
        for (let i=0; i<ts.fills.length; i++)
            sum += ts.fills[i].filled
        const completed = (amountBigInt - sum) < status.order.minFillAmount
        const extraText = completed ? '✓' : null
        const textLocation = breakout === buy ? 'above' : 'below'
        // line active
        if (slope === 0) {
            let price = intercept
            price *= scale
            // horizontal line
            // console.log('hline', price)
            const model = {
                price, breakout, color, extraText, textLocation,
                allocation, maxAllocation, amount, amountSymbol, buy,
            }
            const s = new HLine(model, null, null, null, true)
            this.shapes.push(s)
        } else {
            // diagonal line
            let startTime = t.startTime
            if (startTime === DISTANT_PAST)
                startTime = timestamp() - useChartOrderStore().intervalSecs * 20 // 20 bars ago
            let endTime = t.endTime
            if (endTime === DISTANT_FUTURE)
                endTime = timestamp() // use "now" as the drawing point's time
            let startPrice = (intercept + slope * startTime);
            let endPrice = (intercept + slope * endTime);
            startPrice *= scale
            endPrice *= scale
            // console.log('dline', startTime, endTime, DISTANT_FUTURE, startPrice, endPrice)
            // noinspection EqualityComparisonWithCoercionJS
            const model = {
                pointA: {time: startTime, price: startPrice},
                pointB: {time: endTime, price: endPrice},
                extendLeft: t.startTime === DISTANT_PAST,
                extendRight: t.endTime === DISTANT_FUTURE,
                breakout, color, extraText, textLocation,
                allocation, maxAllocation, amount, amountSymbol, buy,
            }
            const s = new DLine(model, null, null, null, true)
            this.shapes.push(s)
        }
    }

    // todo like this?
    show() {for (const s of this.shapes) s.show();}
    hide() {for (const s of this.shapes) s.hide();}
    delete() {
        for (const s of this.shapes)
            s.delete();
        this.shapes=[]
        for (const s of this.fills)
            deleteShapeId(s)
    }
}
