import React, { useEffect, useMemo } from "react";
import { fromCurve } from "@strata-foundation/spl-token-bonding";
import { useQueryString } from "@strata-foundation/react";
import { Box, SimpleGrid, VStack, Input } from "@chakra-ui/react";
import { CartesianGrid, Legend, Line, LineChart, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts";
import { useVariables } from "../../theme/Root/variables";
const NUM_DATAPOINTS = 40;
export const PriceVsSupplyDisplay = ({ curveConfig, timeOffset, setTimeOffset, reserves, setReserves, startSupply, setStartSupply, endSupply, setEndSupply, setSupply, supply, supplyOffset, }) => {
    const startSupplyNum = Number(startSupply) + supplyOffset;
    const endSupplyNum = Number(endSupply) + supplyOffset;
    const supplyNum = Number(supply);
    const data = useMemo(() => {
        const beforeCurrPoint = [];
        const step = (endSupplyNum - startSupplyNum) / NUM_DATAPOINTS;
        if (step <= 0) {
            return [];
        }
        let tempReserves = Number(reserves);
        for (let i = supplyNum; i > startSupplyNum; i -= step) {
            const currPricing = fromCurve(curveConfig, tempReserves, i, 0);
            const decr = currPricing.sellTargetAmount(step, 0, 0, Number(timeOffset));
            tempReserves -= decr;
            const price = fromCurve(curveConfig, tempReserves, i - step, 0).buyTargetAmount(1, 0, 0, Number(timeOffset));
            if (i - step >= 0 && price > 0) {
                beforeCurrPoint.push({
                    supply: i - step,
                    price,
                    total: tempReserves,
                });
            }
        }
        const afterCurrPoint = [];
        let tempReserves2 = Number(reserves);
        if (startSupplyNum > supplyNum) {
            tempReserves2 =
                tempReserves2 +
                    fromCurve(curveConfig, tempReserves2, Number(supply), 0).buyTargetAmount(startSupplyNum - supplyNum, 0, 0, Number(timeOffset));
        }
        for (let i = Math.max(supplyNum, startSupplyNum); i <= endSupplyNum; i += step) {
            const currPricing = fromCurve(curveConfig, tempReserves2, i, 0);
            const price = currPricing.buyTargetAmount(1, 0, 0, Number(timeOffset));
            const incr = currPricing.buyTargetAmount(step, 0, 0, Number(timeOffset));
            afterCurrPoint.push({
                supply: i,
                price,
                total: tempReserves2,
            });
            tempReserves2 += incr;
        }
        return [...beforeCurrPoint.reverse(), ...afterCurrPoint];
    }, [startSupply, curveConfig, reserves, supply, timeOffset, endSupply]);
    return (React.createElement(VStack, { justify: "stretch" },
        React.createElement(Box, { w: "full", h: "500px" },
            React.createElement(ResponsiveContainer, { width: "100%", height: "100%" },
                React.createElement(LineChart, { width: 500, height: 300, data: data, margin: {
                        top: 5,
                        right: 30,
                        left: 20,
                        bottom: 5,
                    } },
                    React.createElement(ReferenceLine, { x: Number(supply), stroke: "orange" }),
                    React.createElement(CartesianGrid, { strokeDasharray: "3 3" }),
                    React.createElement(XAxis, { tickFormatter: (amount) => Math.floor(amount - supplyOffset).toString(), tickCount: 10, type: "number", dataKey: "supply", label: { value: "Supply", dy: 10 }, domain: [startSupplyNum, endSupplyNum] }),
                    React.createElement(YAxis, { type: "number", label: {
                            value: "Price",
                            position: "insideLeft",
                            angle: -90,
                            dy: 0,
                        } }),
                    React.createElement(Tooltip, null),
                    React.createElement(Legend, null),
                    React.createElement(Line, { activeDot: {
                            onClick: (e, payload) => {
                                //@ts-ignore
                                setSupply(payload.payload.supply);
                                //@ts-ignore
                                setReserves(payload.payload.total);
                            },
                        }, type: "monotone", dataKey: "price", stroke: "#8884d8" }))))));
};
export const RateVsTimeDisplay = ({ curveConfig, reserves, supply, setTimeOffset, maxTime, timeOffset, setMaxTime, }) => {
    const data = useMemo(() => {
        const step = Number(maxTime) / NUM_DATAPOINTS;
        const ret = [];
        for (let i = 0; i < Number(maxTime); i += step) {
            const currPricing = fromCurve(curveConfig, Number(reserves), Number(supply), 0);
            const incAmt = 1;
            const plusOnePricing = fromCurve(curveConfig, Number(reserves) + currPricing.buyTargetAmount(incAmt, 0, 0, Number(i)), Number(supply + incAmt), 0);
            const priceIncrease = plusOnePricing.buyTargetAmount(incAmt, 0, 0, Number(i)) -
                currPricing.buyTargetAmount(incAmt, 0, 0, Number(i));
            const priceFall = currPricing.buyTargetAmount(incAmt, 0, 0, Number(i)) -
                currPricing.buyTargetAmount(incAmt, 0, 0, Number(i + 1));
            ret.push({
                rate: priceFall / priceIncrease,
                timeOffset: i,
            });
        }
        return ret;
    }, [curveConfig, reserves, supply, maxTime]);
    return (React.createElement(VStack, { justify: "stretch" },
        React.createElement(Box, { w: "full", h: "500px" },
            React.createElement(ResponsiveContainer, { width: "100%", height: "100%" },
                React.createElement(LineChart, { width: 500, height: 300, data: data, margin: {
                        top: 5,
                        right: 30,
                        left: 20,
                        bottom: 5,
                    } },
                    React.createElement(ReferenceLine, { x: Number(timeOffset), stroke: "orange" }),
                    React.createElement(CartesianGrid, { strokeDasharray: "3 3" }),
                    React.createElement(XAxis, { tickCount: 10, type: "number", tickFormatter: (seconds) => Math.floor(formatTime(seconds, Number(maxTime))).toString(), dataKey: "timeOffset", label: { value: `Time (${getUnit(Number(maxTime))})`, dy: 10 }, domain: [0, Number(maxTime)] }),
                    React.createElement(YAxis, { type: "number", label: {
                            value: "Breakeven Sell Rate (units/sec)",
                            position: "insideLeft",
                            angle: -90,
                            dy: 0,
                        } }),
                    React.createElement(Tooltip, null),
                    React.createElement(Legend, null),
                    React.createElement(Line, { activeDot: {
                            onClick: (e, payload) => {
                                //@ts-ignore
                                setTimeOffset(payload.payload.timeOffset);
                            },
                        }, type: "monotone", dataKey: "rate", stroke: "#8884d8" }))))));
};
export const EstimatedSalesVsTime = ({ curveConfig, reserves, supply, setTimeOffset, maxTime, timeOffset, setMaxTime, endSupply, }) => {
    const data = useMemo(() => {
        const step = Number(maxTime) / NUM_DATAPOINTS;
        const ret = [];
        for (let i = 0; i < Number(maxTime); i += step) {
            const currPricing = fromCurve(curveConfig, Number(reserves), Number(supply), 0);
            const incAmt = 1;
            const plusOnePricing = fromCurve(curveConfig, Number(reserves) + currPricing.buyTargetAmount(incAmt, 0, 0, Number(i)), Number(supply + incAmt), 0);
            const priceIncrease = plusOnePricing.buyTargetAmount(incAmt, 0, 0, Number(i)) -
                currPricing.buyTargetAmount(incAmt, 0, 0, Number(i));
            const priceFall = currPricing.buyTargetAmount(incAmt, 0, 0, Number(i)) -
                currPricing.buyTargetAmount(incAmt, 0, 0, Number(i + 1));
            const prev = ret[ret.length - 1] ? ret[ret.length - 1].total : 0;
            let total = (priceFall / priceIncrease) * step + prev;
            ret.push({
                total: total > Number(endSupply) ? 0 : total,
                timeOffset: i,
            });
        }
        return ret;
    }, [curveConfig, reserves, supply, maxTime]);
    return (React.createElement(VStack, { justify: "stretch" },
        React.createElement(Box, { w: "full", h: "500px" },
            React.createElement(ResponsiveContainer, { width: "100%", height: "100%" },
                React.createElement(LineChart, { width: 500, height: 300, data: data, margin: {
                        top: 5,
                        right: 30,
                        left: 20,
                        bottom: 5,
                    } },
                    React.createElement(ReferenceLine, { x: Number(timeOffset), stroke: "orange" }),
                    React.createElement(CartesianGrid, { strokeDasharray: "3 3" }),
                    React.createElement(XAxis, { tickCount: 10, type: "number", tickFormatter: (seconds) => Math.floor(formatTime(seconds, Number(maxTime))).toString(), dataKey: "timeOffset", label: { value: `Time (${getUnit(Number(maxTime))})`, dy: 10 }, domain: [0, Number(maxTime)] }),
                    React.createElement(YAxis, { type: "number", label: {
                            value: "Estimated Total Sales",
                            position: "insideLeft",
                            angle: -90,
                            dy: 0,
                        } }),
                    React.createElement(Tooltip, null),
                    React.createElement(Legend, null),
                    React.createElement(Line, { activeDot: {
                            onClick: (e, payload) => {
                                //@ts-ignore
                                setTimeOffset(payload.payload.timeOffset);
                            },
                        }, type: "monotone", dataKey: "total", stroke: "#8884d8" }))))));
};
export const PriceVsTimeDisplay = ({ curveConfig, reserves, supply, setTimeOffset, maxTime, timeOffset, setMaxTime, }) => {
    const data = useMemo(() => {
        const step = Number(maxTime) / NUM_DATAPOINTS;
        const ret = [];
        for (let i = 0; i < Number(maxTime); i += step) {
            const currPricing = fromCurve(curveConfig, Number(reserves), Number(supply), 0);
            const price = currPricing.buyTargetAmount(1, 0, 0, Number(i));
            ret.push({
                price,
                timeOffset: i,
            });
        }
        return ret;
    }, [curveConfig, reserves, supply, maxTime]);
    return (React.createElement(VStack, { justify: "stretch" },
        React.createElement(Box, { w: "full", h: "500px" },
            React.createElement(ResponsiveContainer, { width: "100%", height: "100%" },
                React.createElement(LineChart, { width: 500, height: 300, data: data, margin: {
                        top: 5,
                        right: 30,
                        left: 20,
                        bottom: 5,
                    } },
                    React.createElement(ReferenceLine, { x: Number(timeOffset), stroke: "orange" }),
                    React.createElement(CartesianGrid, { strokeDasharray: "3 3" }),
                    React.createElement(XAxis, { tickCount: 10, type: "number", tickFormatter: (seconds) => Math.floor(formatTime(seconds, Number(maxTime))).toString(), dataKey: "timeOffset", label: { value: `Time (${getUnit(Number(maxTime))})`, dy: 10 }, domain: [0, Number(maxTime)] }),
                    React.createElement(YAxis, { type: "number", label: {
                            value: "Price",
                            position: "insideLeft",
                            angle: -90,
                            dy: 0,
                        } }),
                    React.createElement(Tooltip, null),
                    React.createElement(Legend, null),
                    React.createElement(Line, { activeDot: {
                            onClick: (e, payload) => {
                                //@ts-ignore
                                setTimeOffset(payload.payload.timeOffset);
                            },
                        }, type: "monotone", dataKey: "price", stroke: "#8884d8" }))))));
};
function getUnit(maxTime) {
    if (maxTime < 60) {
        return "seconds";
    }
    else if (maxTime < 60 * 60) {
        return "minutes";
    }
    else if (maxTime < 60 * 60 * 72) {
        return "hours";
    }
    else {
        return "days";
    }
}
/**
 * Seconds from 0 to 60
 * Minutes from 0 to 60 minutes
 * Hours from 0 to 72 hours
 * Days onward
 */
function formatTime(time, maxTime) {
    const unit = getUnit(maxTime);
    if (unit === "seconds") {
        return time;
    }
    else if (unit === "minutes") {
        return time / 60;
    }
    else if (unit === "hours") {
        return time / (60 * 60);
    }
    else {
        return time / (60 * 60 * 24);
    }
}
export const CurveConfiguratorFromVariables = ({ priceVsSupply = true, priceVsTime = true, rateVsTime = false, salesVsTime = false }) => {
    const { curveConfig, startSupply: passedStartSupply, endSupply: passedEndSupply, reserves: passedReserves, supply: passedSupply, maxTime: passedMaxTime, supplyOffset, } = useVariables();
    const [timeOffset, setTimeOffset] = useQueryString("timeOffset", "0");
    const [reserves, setReserves] = useQueryString("reserves", "0");
    const [supply, setSupply] = useQueryString("supply", "0");
    const [startSupply, setStartSupply] = useQueryString("startSupply", passedStartSupply || "0");
    const [endSupply, setEndSupply] = useQueryString("endSupply", passedEndSupply || "100");
    const [maxTime, setMaxTime] = useQueryString("maxTime", passedMaxTime || "10");
    const args = {
        timeOffset,
        setTimeOffset,
        reserves,
        setReserves,
        supply,
        setSupply,
        startSupply,
        setStartSupply,
        endSupply,
        setEndSupply,
        maxTime,
        setMaxTime,
    };
    const numCharts = +priceVsSupply + +priceVsTime + +rateVsTime;
    useEffect(() => {
        passedReserves && setReserves(passedReserves);
    }, [passedReserves]);
    useEffect(() => {
        passedSupply && setSupply(passedSupply);
    }, [passedSupply]);
    useEffect(() => {
        passedEndSupply && setEndSupply(passedEndSupply);
    }, [passedEndSupply]);
    useEffect(() => {
        passedStartSupply && setStartSupply(passedStartSupply);
    }, [passedStartSupply]);
    useEffect(() => {
        passedMaxTime && setMaxTime(passedMaxTime);
    }, [passedMaxTime]);
    const rawCurve = curveConfig?.toRawConfig();
    if (!curveConfig) {
        return React.createElement(Box, { padding: 4, mb: 4, rounded: "lg", backgroundColor: "gray.200" }, "Run the above code block to show curve");
    }
    return (React.createElement(VStack, { w: "full" },
        React.createElement(SimpleGrid, { columns: 3, w: "full" },
            React.createElement(VStack, null,
                React.createElement("label", null, "Reserves"),
                React.createElement(Input, { type: "number", step: 0.001, onChange: (e) => setReserves(e.target.value), value: reserves })),
            React.createElement(VStack, null,
                React.createElement("label", null, "Supply"),
                React.createElement(Input, { type: "number", step: 0.001, onChange: (e) => setSupply(e.target.value), value: supply })),
            React.createElement(VStack, null,
                React.createElement("label", null, "Seconds since Launch"),
                React.createElement(Input, { type: "number", step: 1, onChange: (e) => setTimeOffset(e.target.value), value: timeOffset })),
            React.createElement(VStack, null,
                React.createElement("label", null, "Start Supply"),
                React.createElement(Input, { type: "number", step: 0.001, onChange: (e) => setStartSupply(e.target.value), value: startSupply })),
            React.createElement(VStack, null,
                React.createElement("label", null, "End Supply"),
                React.createElement(Input, { type: "number", step: 0.001, onChange: (e) => setEndSupply(e.target.value), value: endSupply })),
            React.createElement(VStack, null,
                React.createElement("label", null, "Max Time"),
                React.createElement(Input, { type: "number", step: 1, onChange: (e) => setMaxTime(e.target.value), value: maxTime }))),
        React.createElement(SimpleGrid, { columns: [1, 1, 1, Math.min(numCharts, 2)], w: "full" },
            priceVsSupply && (React.createElement(PriceVsSupplyDisplay, { supplyOffset: supplyOffset || 0, curveConfig: rawCurve, ...args })),
            priceVsTime && React.createElement(PriceVsTimeDisplay, { curveConfig: rawCurve, ...args }),
            rateVsTime && React.createElement(RateVsTimeDisplay, { curveConfig: rawCurve, ...args }),
            salesVsTime && (React.createElement(EstimatedSalesVsTime, { curveConfig: rawCurve, ...args })))));
};
//# sourceMappingURL=index.js.map