#pragma once
#include "../log.hpp"
#include "../db/pool.hpp"
#include "nlohmann/json.hpp"
#include "shop.hpp"
#include "customer_groups.hpp"
#include "../config.hpp"
#include "../http.hpp"
#include <cmath>

static const char* PRODUCT_LIST_SQL =
    "WITH TaxRates AS ("
    "  SELECT kSteuerklasse, fSteuersatz FROM dbo.tSteuersatz"
    "  WHERE kSteuerzone IN (SELECT kSteuerzone FROM dbo.tSteuerzone WHERE cName = ?)"
    "), ImageRV AS ("
    "  SELECT kArtikel, MAX(CONVERT(BIGINT, bRowversion)) AS maxImageRV"
    "  FROM dbo.tArtikelbildPlattform WHERE kShop = ? GROUP BY kArtikel"
    ") SELECT TOP (?) "
    "a.kArtikel AS id, a.cArtNr AS sku, ab.cName AS name, "
    "a.fVKNetto AS netPrice, tr.fSteuersatz AS taxRate, "
    "a.dErstelldatum AS createdAt, "
    "CASE WHEN ir.maxImageRV IS NOT NULL AND ir.maxImageRV > CONVERT(BIGINT, a.bRowversion) "
    "THEN ir.maxImageRV ELSE CONVERT(BIGINT, a.bRowversion) END AS lastChanged, "
    "(SELECT TOP 1 img.cHash FROM dbo.tArtikelbildPlattform abp "
    "INNER JOIN dbo.tBild img ON img.kBild = abp.kBild "
    "WHERE abp.kArtikel = a.kArtikel ORDER BY abp.nNr) AS imgHash, "
    "(SELECT STRING_AGG(CAST(ka.kKategorie AS varchar(20)), ',') "
    "FROM dbo.tkategorieartikel ka WHERE ka.kArtikel = a.kArtikel) AS categoryIds, "
    "a.nIstVater AS isParent, a.kVaterArtikel AS parentArticleId, "
    "CASE WHEN a.kStueckliste <> 0 THEN '1' ELSE '0' END AS isCompositeProduct, "
    "(SELECT TOP 1 pv.cVariantName FROM Pos.vProductVariant pv "
    "WHERE pv.kProduct = a.kArtikel) AS variantName "
    "FROM dbo.tArtikel a "
    "INNER JOIN dbo.tArtikelBeschreibung ab ON ab.kArtikel = a.kArtikel AND ab.kSprache = ? "
    "LEFT JOIN TaxRates tr ON tr.kSteuerklasse = a.kSteuerklasse "
    "LEFT JOIN ImageRV ir ON ir.kArtikel = a.kArtikel "
    "WHERE a.cAktiv = 'Y' "
    "AND (? = 0 OR EXISTS (SELECT 1 FROM dbo.tKategorieArtikel ka "
    "INNER JOIN dbo.tKategorieShop ks ON ks.kKategorie = ka.kKategorie AND ks.kShop = ? "
    "WHERE ka.kArtikel = a.kArtikel)) "
    "AND (CONVERT(BIGINT, a.bRowversion) > ? "
    "OR (ir.maxImageRV IS NOT NULL AND ir.maxImageRV > ?)) "
    "ORDER BY lastChanged ASC";

static std::string gross_price(const std::string& net, const std::string& tax) {
    double n = std::stod(net.empty() ? "0" : net);
    double t = std::stod(tax.empty() ? "0" : tax);
    char buf[32];
    std::snprintf(buf, sizeof(buf), "%.2f", n * (1.0 + t / 100.0));
    return buf;
}

inline nlohmann::json get_product_list(int64_t cursor, int limit) {
    std::string tax_zone = config::get("TAX_ZONE_NAME", "Zone-EU");
    int lang = config::get_int("LANGUAGE_ID", 1);
    int shop = get_active_shop_id();

    std::vector<Param> ps = {
        {ParamType::NVarChar, tax_zone, 0},
        {ParamType::Int,"",shop},
        {ParamType::Int,"",limit},
        {ParamType::Int,"",lang},
        {ParamType::Int,"",shop},
        {ParamType::Int,"",shop},
        {ParamType::BigInt,"",cursor},
        {ParamType::BigInt,"",cursor}
    };
    ResultSet rs;
    if (!get_pool().execute(PRODUCT_LIST_SQL, ps, rs)) {
        logc::warn("product list query failed (cursor=%lld limit=%d shop=%d)",
                   (long long)cursor, limit, shop);
        return nlohmann::json::array();
    }

    auto cg_ids = get_customer_group_ids();
    nlohmann::json result = nlohmann::json::array();

    for (auto& row : rs) {
        std::string base_price = gross_price(row[3].str, row[4].str);
        std::string cats_raw = row[8].str;
        std::vector<std::string> cat_ids;
        if (!cats_raw.empty()) {
            size_t pos = 0;
            while ((pos = cats_raw.find(',')) != std::string::npos) {
                cat_ids.push_back(cats_raw.substr(0, pos));
                cats_raw.erase(0, pos + 1);
            }
            cat_ids.push_back(cats_raw);
        }

        nlohmann::json cats = nlohmann::json::array();
        for (auto& c : cat_ids) cats.push_back({{"categoryId", c}});

        nlohmann::json prices = nlohmann::json::array();
        for (auto& cgid : cg_ids) {
            prices.push_back({
                {"customerGroupId", std::to_string(cgid)},
                {"customerId", "0"},
                {"price", base_price},
                {"quantity", "0"}
            });
        }

        nlohmann::json product = {
            {"_id", row[0].str},
            {"imghash", row[7].str.empty() ? nullptr : nlohmann::json(row[7].str)},
            {"imgsrc", row[7].str.empty() ? nullptr : nlohmann::json(row[7].str)},
            {"sku", row[1].str},
            {"name", row[2].str},
            {"tax_rate", std::to_string((int)std::round(std::stod(row[4].str.empty()?"0":row[4].str)))},
            {"price", base_price},
            {"created_at", row[5].str},
            {"lastChanged", row[6].str},
            {"categories_id", cat_ids.empty() ? "0" : cat_ids[0]},
            {"categories", cats},
            {"prices", prices},
            {"is_parent", row[9].str == "1" ? "1" : "0"},
            {"parent", std::stoll(row[10].str) > 0 ? row[10].str : "0"},
            {"variants", row[12].str},
            {"isCompositeProduct", row[11].str},
            {"attributes", nlohmann::json::array()},
            {"sort", "0"},
            {"p_price", "0.00"},
            {"discountable", "0"},
            {"deposit", "0"},
            {"discount", ""},
            {"d_price", "0.0"},
            {"tax_rate2", ""},
            {"use_in_out_tax", "0"},
            {"barcode", nullptr},
            {"use_stock", "0"},
            {"q_div", "0"},
            {"quantity", "0"},
            {"unit", nullptr},
            {"single_bookable", "0"},
            {"annotation", ""},
            {"status", "0"},
            {"tags", ""},
            {"variants", row[12].str},
            {"print_kitchen_receipt", "0"},
            {"deposit_name", ""},
            {"updated_at", "0001-01-01 00:00:00"},
            {"configurationGroups", ""},
            {"options", nullptr},
            {"hasBestBeforeDate", "0"},
            {"hasLotNumber", "0"},
            {"hasSerialNumber", "0"},
            {"PLU", ""},
            {"short_description", ""},
            {"minStock", "0"},
            {"container", nlohmann::json::array()},
            {"reservedQuantity", "0.00"},
            {"deliveryDetails", nlohmann::json::array()},
            {"isbn", ""},
            {"manufacturerName", nullptr},
            {"han", nullptr},
            {"productType", "0"},
            {"voucherData", nullptr},
            {"inputPrice", "0"},
            {"inputQuantity", "0"}
        };
        result.push_back(std::move(product));
    }
    return result;
}
