#include <iostream>
#include <string>

#include "BacktestWrapper.h"
#include "Concepts.h"
#include "CoreConcept.h"
#include "CryptoBacktest.h"
#include "DDBPlugin.h"
#include "Exceptions.h"
#include "OperatorImp.h"
#include "ScalarImp.h"
#include "SmartPointer.h"
#include "Swordfish.h"
#include "SwordfishFunctionDef.h"
#include "Types.h"
#include "Util.h"

using namespace std;

ConstantSP initialize(vector<ConstantSP> &arguments) {
    cout << "initialize" << endl;
    DictionarySP contextDict = arguments[0];

    contextDict->set("initPrice", Util::createDictionary(DT_SYMBOL, nullptr, DT_ANY, nullptr));
    contextDict->set("alpha", new Double(0.01));
    contextDict->set("beta", new Double(0.005));
    contextDict->set("M", new Long(100000));
    contextDict->set("baseBuyPrice", Util::createDictionary(DT_SYMBOL, nullptr, DT_ANY, nullptr));
    contextDict->set("baseSellPrice", Util::createDictionary(DT_SYMBOL, nullptr, DT_ANY, nullptr));
    contextDict->set("highPrice", Util::createDictionary(DT_SYMBOL, nullptr, DT_ANY, nullptr));
    contextDict->set("lowPrice", Util::createDictionary(DT_SYMBOL, nullptr, DT_ANY, nullptr));
    contextDict->set("N", Util::createDictionary(DT_SYMBOL, nullptr, DT_ANY, nullptr));
    contextDict->set("feeRatio", new Double(0.00015));
    cout << contextDict->getMember("Universe")->getString() << endl;
    BacktestWrapper::setUniverse(contextDict->getMember("engine"), contextDict->getMember("Universe"));

    return new Void();
}

ConstantSP beforeTrading(vector<ConstantSP> &arguments) {
    cout << "beforeTrading" << endl;
    return new Void();
}

void updateBaseBuyPrice(ConstantSP istock, ConstantSP lastPrice, ConstantSP basePrice, DictionarySP baseBuyPrice,
                        DictionarySP baseSellPrice, DictionarySP N, DictionarySP highPrice, DictionarySP lowPrice,
                        ConstantSP alpha, ConstantSP n, int mode = 0) {
    baseBuyPrice->set(istock, OperatorImp::multiply(basePrice, OperatorImp::sub(new Double(1.0), alpha)));
    baseSellPrice->set(istock, OperatorImp::multiply(basePrice, OperatorImp::add(new Double(1.0), alpha)));
    N->set(istock, n);
    if (mode == 0) {
        lowPrice->set(istock, new Double(0.0));
        highPrice->set(istock, new Double(10000.0));
    } else if (mode == 1) {
        lowPrice->set(istock, lastPrice);
        highPrice->set(istock, new Double(10000.0));
    } else if (mode == 2) {
        lowPrice->set(istock, new Double(0.0));
        highPrice->set(istock, lastPrice);
    }
}

ConstantSP onBar(vector<ConstantSP> &arguments) {
    // cout << "onBar" << endl;
    DictionarySP contextDict = arguments[0];
    DictionarySP msg = arguments[1];
    // cout << "msg: " << endl << msg->getString() << endl;
    // cout << "contextDict: " << endl << contextDict->getString() << endl;

    ConstantSP expireTime = OperatorImp::now(Expression::void_, Expression::void_);
    ConstantSP upperLimit = new Double(0.0);
    ConstantSP lowerLimit = new Double(0.0);
    ConstantSP slippage = new Double(0.0);

    ConstantSP alpha = contextDict->getMember("alpha");
    // 回落间距（百分数）
    ConstantSP beta = contextDict->getMember("beta");
    ConstantSP M = contextDict->getMember("M");
    ConstantSP N = contextDict->getMember("N");
    ConstantSP initPrice = contextDict->getMember("initPrice");
    ConstantSP baseBuyPrice = contextDict->getMember("baseBuyPrice");
    ConstantSP baseSellPrice = contextDict->getMember("baseSellPrice");
    ConstantSP highPrice = contextDict->getMember("highPrice");
    ConstantSP lowPrice = contextDict->getMember("lowPrice");

    VectorSP keys = msg->keys();
    for (int i = 0; i < keys->size(); i++) {
        ConstantSP isymbol = keys->get(i);
        ConstantSP istock = isymbol;
        ConstantSP lastPrice = msg->getMember(isymbol)->getMember("close");

        ConstantSP resultSP = new Bool(false);
        initPrice->contain(istock, resultSP);
        // 设置初始价
        if (!resultSP->getBool()) {
            initPrice->set(istock, lastPrice);
            updateBaseBuyPrice(istock, lastPrice, lastPrice, baseBuyPrice, baseSellPrice, N, highPrice, lowPrice, alpha,
                               new Long(1), 0);
        }
        ConstantSP initPrice_ = initPrice->getMember(istock);
        if (lastPrice->getDouble() <= baseBuyPrice->getMember(istock)->getDouble()) {
            // 下跌,更新下网格线
            unordered_map<string, ConstantSP> args = {
                {"lastPrice", lastPrice}, {"initPrice_", initPrice_}, {"alpha", alpha}};
            ConstantSP n = DolphinDBLib::execute("floor(log(lastPrice\\initPrice_)\\log(1-alpha))+1", args);
            if (n->getDouble() > N->getMember(istock)->getDouble()) {
                ConstantSP newBasePrice =
                    OperatorImp::multiply(initPrice_, OperatorImp::power(OperatorImp::sub(new Long(1), alpha), n));
                updateBaseBuyPrice(istock, lastPrice, newBasePrice, baseBuyPrice, baseSellPrice, N, highPrice, lowPrice,
                                   alpha, n, 1);
            }
        } else if (lastPrice->getDouble() > baseSellPrice->getMember(istock)->getDouble()) {
            // 上涨，更新上网格线
            unordered_map<string, ConstantSP> args = {
                {"lastPrice", lastPrice}, {"initPrice_", initPrice_}, {"alpha", alpha}};
            ConstantSP n = DolphinDBLib::execute("floor(log(lastPrice\\initPrice_)\\log(1+alpha))+1", args);
            if (n->getDouble() > N->getMember(istock)->getDouble()) {
                ConstantSP newBasePrice =
                    OperatorImp::multiply(initPrice_, OperatorImp::power(OperatorImp::add(new Long(1), alpha), n));
                updateBaseBuyPrice(istock, lastPrice, newBasePrice, baseBuyPrice, baseSellPrice, N, highPrice, lowPrice,
                                   alpha, n, 2);
            }
        }
        ConstantSP source = msg->getMember(isymbol)->getMember("symbolSource");
        if (lowPrice->getMember(istock)->getDouble() > 0 and
            lastPrice->getDouble() > lowPrice->getMember(istock)->getDouble() * (1 + beta->getDouble())) {
            // 买入
            ConstantSP qty = new Double(0.001);
            VectorSP msg_ = Util::createVector(DT_ANY, 12);
            msg_->set(0, istock);
            msg_->set(1, source);
            msg_->set(2, contextDict->getMember("tradeTime"));
            msg_->set(3, new Long(5));
            msg_->set(4, lastPrice);
            msg_->set(5, upperLimit);
            msg_->set(6, lowerLimit);
            msg_->set(7, qty);
            msg_->set(8, new Long(1));
            msg_->set(9, slippage);
            msg_->set(10, new Long(1));
            msg_->set(11, expireTime);
            ConstantSP orderId = BacktestWrapper::submitOrder(contextDict->getMember("engine"), msg_, new String("buy"),
                                                              new Int(0), new String("futures"));

            initPrice->set(istock, lastPrice);
            updateBaseBuyPrice(istock, lastPrice, lastPrice, baseBuyPrice, baseSellPrice, N, highPrice, lowPrice, alpha,
                               new Long(1), 0);
        } else if (highPrice->getMember(istock)->getDouble() < 10000.0 and
                   lastPrice->getDouble() < highPrice->getMember(istock)->getDouble() * (1 - beta->getDouble())) {
            // 卖出
            ConstantSP qty = BacktestWrapper::getPosition(contextDict->getMember("engine"), istock)
                                 ->getMember("todayBuyVolume")
                                 ->get(0);
            if (qty->getLong() <= 0) {
                continue;
            }
            qty = new Double(0.001);
            VectorSP msg_ = Util::createVector(DT_ANY, 12);
            msg_->set(0, istock);
            msg_->set(1, source);
            msg_->set(2, contextDict->getMember("tradeTime"));
            msg_->set(3, new Long(5));
            msg_->set(4, lastPrice);
            msg_->set(5, upperLimit);
            msg_->set(6, lowerLimit);
            msg_->set(7, qty);
            msg_->set(8, new Long(3));
            msg_->set(9, slippage);
            msg_->set(10, new Long(1));
            msg_->set(11, expireTime);
            ConstantSP orderId = BacktestWrapper::submitOrder(contextDict->getMember("engine"), msg_,
                                                              new String("sell"), new Int(0), new String("futures"));
            updateBaseBuyPrice(istock, lastPrice, lastPrice, baseBuyPrice, baseSellPrice, N, highPrice, lowPrice, alpha,
                               new Long(1), 0);
        }
        // 实时更新最低或者最高价
        if (lowPrice->getMember(istock)->getDouble() > 0) {
            lowPrice->set(istock, OperatorImp::min(lastPrice, lowPrice->getMember(istock)));
        }
        if (highPrice->getMember(istock)->getDouble() < 10000.0) {
            highPrice->set(istock, OperatorImp::max(lastPrice, highPrice->getMember(istock)));
        }
    }

    contextDict->set("initPrice", initPrice);
    contextDict->set("baseBuyPrice", baseBuyPrice);
    contextDict->set("N", N);
    contextDict->set("highPrice", highPrice);
    contextDict->set("lowPrice", lowPrice);
    contextDict->set("baseSellPrice", baseSellPrice);

    return new Void();
}

ConstantSP onOrder(vector<ConstantSP> &arguments) {
    // cout << "onOrder" << endl;
    return new Void();
}

ConstantSP onTrade(vector<ConstantSP> &arguments) {
    // cout << "onTrade" << endl;
    return new Void();
}

ConstantSP finalize(vector<ConstantSP> &arguments) {
    cout << "finalize" << endl;
    return new Void();
}

int main() {
    DolphinDBLib::initializeRuntime();
    std::cout << "Swordfish version: " << DolphinDBLib::execute("version()")->getString() << std::endl;

    SmartPointer<DDBConnection> conn = new DDBConnection("192.198.1.32", 8742, "admin", "DolphinDB123456");
    long long startTime = DolphinDBLib::execute("now()")->getLong();

    auto _ = DDBPlugin("MatchingEngineSimulator");  // 回测插件依赖模拟撮合引擎插件

    CryptoBacktest backtest(conn);
    ConstantSP startDate = new Date(2025, 8, 23);
    ConstantSP endDate = new Date(2025, 8, 25);
    ConstantSP strategyName = new String("CryptoBacktest");
    DictionarySP eventCallbacks = Util::createDictionary(DT_STRING, nullptr, DT_ANY, nullptr);
    auto initializeFunc = new SwordfishFunctionDef(initialize, "initialize", 1, 1, false);
    auto beforeTradingFunc = new SwordfishFunctionDef(beforeTrading, "beforeTrading", 1, 1, false);
    auto onBarFunc = new SwordfishFunctionDef(onBar, "onBar", 3, 3, false);
    auto onOrderFunc = new SwordfishFunctionDef(onOrder, "onOrder", 2, 2, false);
    auto onTradeFunc = new SwordfishFunctionDef(onTrade, "onTrade", 2, 2, false);
    auto finalizeFunc = new SwordfishFunctionDef(finalize, "finalize", 1, 1, false);
    eventCallbacks->set("initialize", initializeFunc);
    eventCallbacks->set("beforeTrading", beforeTradingFunc);
    eventCallbacks->set("onBar", onBarFunc);
    eventCallbacks->set("onOrder", onOrderFunc);
    eventCallbacks->set("onTrade", onTradeFunc);
    eventCallbacks->set("finalize", finalizeFunc);
    ConstantSP dataType = new Int(3);
    ConstantSP onBarTarget = new String("onBar");
    ConstantSP onSnapshotTarget = new String("onSnapshot");
    ConstantSP onBarResultSP = new Bool(false);
    ConstantSP onSnapshotResultSP = new Bool(false);
    eventCallbacks->contain(onBarTarget, onBarResultSP);
    eventCallbacks->contain(onSnapshotTarget, onSnapshotResultSP);
    if (onBarResultSP->getBool()) {
        dataType = new Int(3);
        cout << "开始分钟行情类型的数字货币策略：" << endl;
    } else if (onSnapshotResultSP->getBool()) {
        dataType = new Int(1);
        cout << "开始快照行情类型的数字货币策略：" << endl;
    } else {
        throw RuntimeException("没有 onSnapshot 或者 onBar 策略回调函数的定义。");
    }
    DictionarySP userConfig = CryptoBacktest::getUnifiedConfig(startDate, endDate, dataType);

    cout << "开始策略仿真：" << startDate->getString() << ":" << endDate->getString() << endl;
    SmartPointer<BacktestWrapper> engine = backtest.runCryptoBacktest(strategyName, userConfig, eventCallbacks);

    long long endTime = DolphinDBLib::execute("now()")->getLong();
    cout << "total cost: " << double(endTime - startTime) / 1000 << "s" << endl;

    // 获取回测结果
    cout << "getTradeDetails: " << endl << engine->getTradeDetails()->getString() << endl;

    DolphinDBLib::finalizeRuntime();
    return 0;
}