import {uint32max, uint64max} from "@/misc.js";
import {encodeIEE754} from "@/common.js";


export const MAX_FRACTION = 65535;
export const NO_CONDITIONAL_ORDER = uint64max;
export const NO_OCO = uint64max;
export const DISTANT_PAST = 0
export const DISTANT_FUTURE = uint32max

export const MIN_EXECUTION_TIME = 60  // give at least one full minute for each tranche to trigger
export const DEFAULT_SLIPPAGE = 0.0030;
export const MIN_SLIPPAGE = 0.0001;

// struct SwapOrder {
//     address tokenIn;
//     address tokenOut;
//     Route route;
//     uint256 amount;
//     uint256 minFillAmount;  // if a tranche has less than this amount available to fill, it is considered completed
//     bool amountIsInput;
//     bool outputDirectlyToOwner;
//     bool inverted;
//     uint64 conditionalOrder; // use NO_CONDITIONAL_ORDER for no chaining. conditionalOrder index must be < than this order's index for safety (written first) and conditionalOrder state must be Template
//     Tranche[] tranches;
// }
// struct Route {
//     Exchange exchange;
//     uint24 fee;
// }
export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput, inverted, tranches,
                         minFillAmount=null, outputDirectlyToOwner = false, conditionalOrder = NO_CONDITIONAL_ORDER) {
    amountIsInput = !!amountIsInput // force convert to bool
    outputDirectlyToOwner = !!outputDirectlyToOwner // force convert to bool
    amount = BigInt(amount)
    if (!tranches)
        tranches = [newTranche({marketOrder: true})] // todo this is just a swap: issue warning?
    if( minFillAmount === null )
        minFillAmount = amount / 1000n  // default to min trade size of 0.1%
    return {
        tokenIn, tokenOut, route:{exchange, fee},
        amount, minFillAmount, amountIsInput,
        outputDirectlyToOwner, inverted, conditionalOrder, tranches
    }
}

// struct Tranche {
//     uint16  fraction;
//
//     bool   startTimeIsRelative;
//     bool   endTimeIsRelative;
//     bool   minIsBarrier;
//     bool   maxIsBarrier;
//     bool   marketOrder;  // if true, both min and max lines are ignored, and minIntercept is treated as a maximum slippage value (use positive numbers)
//     bool   minIsRatio;
//     bool   maxIsRatio;
//     bool   _reserved7;
//     uint16 rateLimitFraction;
//     uint24 rateLimitPeriod;
//
//     uint32 startTime;  // use DISTANT_PAST to disable
//     uint32 endTime;    // use DISTANT_FUTURE to disable
//
//     // if intercept and slope are both 0 the line is disabled
//     float  minIntercept; // if marketOrder==true, this is the (positive) max slippage amount
//     float  minSlope;
//     float  maxIntercept;
//     float  maxSlope;
// }
export function newTranche({
                            fraction = MAX_FRACTION,
                            marketOrder = false,
                            startTimeIsRelative = false,
                            startTime = DISTANT_PAST,
                            endTimeIsRelative = false,
                            endTime = DISTANT_FUTURE,
                            minIsBarrier = false,
                            minIsRatio = false,
                            slippage = 0, // may also set minIntercept instead
                            minIntercept = 0,
                            minSlope = 0,
                            maxIsBarrier = false,
                            maxIsRatio = false,
                            maxIntercept = 0,
                            maxSlope = 0,
                            rateLimitFraction = 0,
                            rateLimitPeriod = 0,
                           } = {}) {
    if( marketOrder ) {
        if (minIntercept !== 0 || minSlope !== 0 || maxIntercept !== 0 || maxSlope !== 0)
            throw Error('Cannot set line information on a market order')
        if (slippage === 0)
            slippage = DEFAULT_SLIPPAGE
        else if (slippage < MIN_SLIPPAGE)
            slippage = MIN_SLIPPAGE
        minIntercept = encodeIEE754(slippage)  // this is the slippage field for market orders
        minSlope = 0
        maxIntercept = 0
        maxSlope = 0
    }
    else {
        minIntercept = encodeIEE754(minIntercept)
        minSlope = encodeIEE754(minSlope)
        maxIntercept = encodeIEE754(maxIntercept)
        maxSlope = encodeIEE754(maxSlope)
    }
    const minLine = {intercept: minIntercept, slope: minSlope}
    const maxLine = {intercept: maxIntercept, slope: maxSlope}
    return {
        fraction: Math.min(MAX_FRACTION, Math.round(fraction)), marketOrder,
        startTimeIsRelative, startTime, endTimeIsRelative, endTime,
        minIsBarrier, minLine, maxIsBarrier, maxLine,
        minIsRatio, maxIsRatio, _reserved7: false, rateLimitFraction, rateLimitPeriod,
    }
}

export const Exchange = {
    UniswapV2: 0,
    UniswapV3: 1,
}

export const OrderState = {
    Unknown: -1,
    Signing: 0,
    Underfunded: 1,
    Open: 2,
    Canceled: 3,
    Expired: 4,
    Filled: 5,
    Error: 99,
}

export function isOpen(state) {
    return state >= 1 && state < 3
}


export function parseElaboratedOrderStatus(chainId, status) {
    const [txId, ...remaining] = status
    const result = parseOrderStatus(chainId, remaining)
    result.txId = txId
    return result
}


export function parseOrderStatus(chainId, status) {
    // console.log('parseOrderStatus', status)
    let [
        order,
        fillFeeHalfBps,
        state,
        startTime,
        startPrice,
        ocoGroup,
        filledIn,
        filledOut,
        trancheStatus,
    ] = status
    order = parseOrder(order)
    filledIn = BigInt(filledIn)
    filledOut = BigInt(filledOut)
    const filled = order.amountIsInput ? filledIn : filledOut
    trancheStatus = trancheStatus.map((obj)=>parseTrancheStatus(obj, order.amountIsInput))
    const result = {
        chainId, order, fillFeeHalfBps, state, startTime, startPrice, ocoGroup,
        filledIn, filledOut, filled, trancheStatus,
    };
    // console.log('SwapOrderStatus', result)
    return result
}

function parseFill(obj) {
    let [tx, time, filledIn, filledOut, fee] = obj
    // time = new Date(time * 1000)
    filledIn = BigInt(filledIn)
    filledOut = BigInt(filledOut)
    const filled = obj.amountIsInput ? filledIn : filledOut
    fee = BigInt(fee)
    return {tx, time, filledIn, filledOut, filled, fee}
}

function parseTrancheStatus(obj, amountIsInput) {
    let [filledIn, filledOut, activationTime, startTime, endTime, rawFills,] = obj
    filledIn = BigInt(filledIn)
    filledOut = BigInt(filledOut)
    const fills = []
    for (const fill of rawFills)
        fills.push(parseFill(fill, amountIsInput))
    const filled = amountIsInput ? filledIn : filledOut
    return {filledIn, filledOut, filled, activationTime, startTime, endTime, fills}
}

export function parseOrder(order) {
    let [
        tokenIn,
        tokenOut,
        route,
        amount,
        minFillAmount,
        amountIsInput,
        outputDirectlyToOwner,
        inverted,
        conditionalOrder,
        tranches,
    ] = order
    route = parseRoute(route)
    amount = BigInt(amount)
    minFillAmount = BigInt(minFillAmount)
    tranches = tranches.map(parseTranche)
    return {
        tokenIn, tokenOut, route, amount, minFillAmount, amountIsInput, outputDirectlyToOwner, inverted, conditionalOrder, tranches
    }
}

export function parseRoute(route) {
    let [exchange, fee] = route
    return {exchange, fee} // todo enum?
}

export function parseTranche(tranche) {
    let [
        fraction,
        startTimeIsRelative,
        endTimeIsRelative,
        minIsBarrier,
        maxIsBarrier,
        marketOrder,
        minIsRatio,
        maxIsRatio,
        _reserved7,
        rateLimitFraction,
        rateLimitPeriod,
        startTime,
        endTime,
        minLine,
        maxLine,
    ] = tranche
    const [minB,minS] = minLine
    minLine = {intercept: minB, slope: minS }
    const [maxB,maxS] = maxLine
    maxLine = {intercept: maxB, slope: maxS }
    const result = {
        fraction, startTimeIsRelative, endTimeIsRelative, minIsBarrier, maxIsBarrier, marketOrder,
        minIsRatio, maxIsRatio, rateLimitFraction, rateLimitPeriod,
        startTime, endTime, minLine, maxLine,
    }
    // console.log('parseTranche', tranche, result)
    return result
}


export function parseFeeSchedule(sched) {
    const [orderFee, orderExp, gasFee, gasExp, fillFeeHalfBps] = sched
    return {
        orderFee: orderFee << orderExp, // orderFee is in native (ETH) currency
        gasFee: gasFee << gasExp, // gasFee is in native (ETH) currency
        fillFee: fillFeeHalfBps/1_000_000 // fillFee is a multiplier on the filled volume.  0.0001 = 0.1% of the output token taken as a fee
    }
}
