"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.metadata = void 0;
const db_1 = require("@b/db");
const error_1 = require("@b/utils/error");
const sequelize_1 = require("sequelize");
exports.metadata = {
    summary: "Get Ecommerce Dashboard Data",
    description: "Retrieves all key data for the admin ecommerce dashboard.",
    operationId: "getAdminEcommerceDashboard",
    tags: ["Ecommerce", "Admin", "Dashboard"],
    requiresAuth: true,
    logModule: "ADMIN_ECOM",
    logTitle: "Get dashboard data",
    parameters: [
        {
            name: "startDate",
            in: "query",
            required: false,
            schema: { type: "string", format: "date" },
            description: "Start date for chart/statistics range (ISO format)",
        },
        {
            name: "endDate",
            in: "query",
            required: false,
            schema: { type: "string", format: "date" },
            description: "End date for chart/statistics range (ISO format)",
        },
        {
            name: "chartType",
            in: "query",
            required: false,
            schema: { type: "string", enum: ["revenue", "orders", "customers"] },
            description: "Metric to use for the sales chart",
        },
    ],
    responses: {
        200: {
            description: "Ecommerce dashboard data retrieved",
            content: {
                "application/json": { schema: { type: "object" } },
            },
        },
        401: { description: "Unauthorized" },
        500: { description: "Internal Server Error" },
    },
    permission: "access.ecommerce.dashboard",
    demoMask: ["recentOrders.customer.email"],
};
exports.default = async (data) => {
    const { user, query, ctx } = data;
    if (!(user === null || user === void 0 ? void 0 : user.id))
        throw (0, error_1.createError)({ statusCode: 401, message: "Unauthorized" });
    ctx === null || ctx === void 0 ? void 0 : ctx.step("Parsing date range and chart parameters");
    const now = new Date();
    const { startDate, endDate, chartType: chartTypeRaw } = query || {};
    let start = startDate ? new Date(startDate) : new Date(now);
    let end = endDate ? new Date(endDate) : now;
    if (!startDate || !endDate) {
        end = now;
        start = new Date(now);
        start.setDate(end.getDate() - 7);
    }
    const chartType = (chartTypeRaw || "revenue");
    const periodMs = end.getTime() - start.getTime();
    const prevStart = new Date(start.getTime() - periodMs);
    const prevEnd = new Date(start.getTime());
    try {
        ctx === null || ctx === void 0 ? void 0 : ctx.step("Fetching products data");
        const productsRaw = await db_1.models.ecommerceProduct.findAll({
            include: [
                {
                    model: db_1.models.ecommerceOrderItem,
                    as: "ecommerceOrderItems",
                    attributes: [],
                },
                {
                    model: db_1.models.ecommerceCategory,
                    as: "category",
                    attributes: ["id", "name"],
                },
            ],
            attributes: {
                include: [
                    [
                        (0, sequelize_1.fn)("COALESCE", (0, sequelize_1.fn)("SUM", (0, sequelize_1.col)("ecommerceOrderItems.quantity")), 0),
                        "soldCount",
                    ],
                ],
            },
            group: ["ecommerceProduct.id", "category.id"],
            raw: false,
            paranoid: false,
        });
        const products = productsRaw.map((p) => ({
            ...p.get({ plain: true }),
            soldCount: Number(p.get("soldCount") || 0),
        }));
        ctx === null || ctx === void 0 ? void 0 : ctx.step("Fetching orders data");
        const ordersRaw = await db_1.models.ecommerceOrder.findAll({
            include: [
                {
                    model: db_1.models.user,
                    as: "user",
                    attributes: ["id", "firstName", "lastName", "email"],
                },
                {
                    model: db_1.models.ecommerceOrderItem,
                    as: "ecommerceOrderItems",
                    attributes: ["productId", "quantity"],
                },
            ],
            order: [["createdAt", "DESC"]],
            paranoid: false,
        });
        const orders = ordersRaw.map((o) => {
            const order = o.get({ plain: true });
            order.customer = order.user
                ? {
                    name: order.user.firstName + " " + order.user.lastName,
                    email: order.user.email,
                }
                : { name: "Guest", email: "" };
            order.total = (order.ecommerceOrderItems || []).reduce((sum, i) => sum + (i.quantity || 0), 0);
            return order;
        });
        ctx === null || ctx === void 0 ? void 0 : ctx.step("Fetching customers data");
        const customersRaw = await db_1.models.user.findAll({
            include: [
                {
                    model: db_1.models.ecommerceOrder,
                    as: "ecommerceOrders",
                    required: true,
                    attributes: [],
                },
            ],
            attributes: [
                "id",
                "firstName",
                "lastName",
                "email",
                "createdAt",
                [(0, sequelize_1.fn)("COUNT", (0, sequelize_1.col)("ecommerceOrders.id")), "orderCount"],
            ],
            group: ["user.id"],
            order: [["createdAt", "DESC"]],
            paranoid: false,
        });
        const customers = customersRaw.map((u) => u.get({ plain: true }));
        ctx === null || ctx === void 0 ? void 0 : ctx.step("Calculating current period statistics");
        const completedOrders = orders.filter((o) => o.status === "COMPLETED" &&
            o.createdAt &&
            o.createdAt >= start &&
            o.createdAt <= end);
        const totalRevenue = completedOrders.reduce((sum, o) => {
            const orderSum = (o.ecommerceOrderItems || []).reduce((os, i) => {
                const prod = products.find((p) => p.id === i.productId);
                return os + ((prod === null || prod === void 0 ? void 0 : prod.price) || 0) * (i.quantity || 0);
            }, 0);
            return sum + orderSum;
        }, 0);
        const ordersInPeriod = orders.filter((o) => o.createdAt && o.createdAt >= start && o.createdAt <= end);
        const totalOrders = ordersInPeriod.length;
        const averageOrderValue = totalOrders > 0 ? totalRevenue / totalOrders : 0;
        const totalUnitsSold = completedOrders.reduce((sum, o) => sum +
            (o.ecommerceOrderItems || []).reduce((os, i) => os + (i.quantity || 0), 0), 0);
        const newCustomersCount = customers.filter((u) => u.createdAt && u.createdAt >= start && u.createdAt <= end).length;
        const pendingOrders = ordersInPeriod.filter((o) => o.status === "PENDING").length;
        const outOfStockCount = products.filter((p) => (p.inventoryQuantity || 0) === 0).length;
        const shippedToday = 0;
        const completedOrdersCount = ordersInPeriod.filter((o) => o.status === "COMPLETED").length;
        ctx === null || ctx === void 0 ? void 0 : ctx.step("Calculating previous period statistics");
        const completedOrdersPrev = orders.filter((o) => o.status === "COMPLETED" &&
            o.createdAt &&
            o.createdAt >= prevStart &&
            o.createdAt < prevEnd);
        const totalRevenuePrev = completedOrdersPrev.reduce((sum, o) => {
            const orderSum = (o.ecommerceOrderItems || []).reduce((os, i) => {
                const prod = products.find((p) => p.id === i.productId);
                return os + ((prod === null || prod === void 0 ? void 0 : prod.price) || 0) * (i.quantity || 0);
            }, 0);
            return sum + orderSum;
        }, 0);
        const ordersPrev = orders.filter((o) => o.createdAt && o.createdAt >= prevStart && o.createdAt < prevEnd).length;
        const avgOrderValuePrev = ordersPrev > 0 ? totalRevenuePrev / ordersPrev : 0;
        const unitsSoldPrev = completedOrdersPrev.reduce((sum, o) => sum +
            (o.ecommerceOrderItems || []).reduce((os, i) => os + (i.quantity || 0), 0), 0);
        const newCustomersPrev = customers.filter((u) => u.createdAt && u.createdAt >= prevStart && u.createdAt < prevEnd).length;
        function calcChange(now, prev) {
            if (prev === 0 && now === 0)
                return 0;
            if (prev === 0)
                return 100;
            return ((now - prev) / prev) * 100;
        }
        const revenueChange = calcChange(totalRevenue, totalRevenuePrev);
        const ordersChange = calcChange(totalOrders, ordersPrev);
        const averageOrderChange = calcChange(averageOrderValue, avgOrderValuePrev);
        const unitsSoldChange = calcChange(totalUnitsSold, unitsSoldPrev);
        const newCustomersChange = calcChange(newCustomersCount, newCustomersPrev);
        ctx === null || ctx === void 0 ? void 0 : ctx.step("Building chart data");
        function buildChartData(key, rangeStart, rangeEnd) {
            const chartLabels = [];
            const chartData = [];
            const dateUnit = rangeEnd.getTime() - rangeStart.getTime() > 40 * 24 * 3600 * 1000
                ? "month"
                : "day";
            const cursor = new Date(rangeStart);
            while (cursor <= rangeEnd) {
                chartLabels.push(dateUnit === "day"
                    ? cursor.toLocaleDateString("en-US", {
                        month: "short",
                        day: "numeric",
                    })
                    : cursor.toLocaleDateString("en-US", {
                        month: "short",
                        year: "numeric",
                    }));
                let value = 0;
                if (key === "revenue" || key === "unitsSold") {
                    const ordersHere = orders.filter((o) => {
                        if (!o.createdAt)
                            return false;
                        const d = new Date(o.createdAt);
                        if (dateUnit === "day")
                            return (d.getFullYear() === cursor.getFullYear() &&
                                d.getMonth() === cursor.getMonth() &&
                                d.getDate() === cursor.getDate());
                        else
                            return (d.getFullYear() === cursor.getFullYear() &&
                                d.getMonth() === cursor.getMonth());
                    });
                    value = ordersHere.reduce((sum, o) => {
                        if (o.status !== "COMPLETED")
                            return sum;
                        if (key === "revenue") {
                            return (sum +
                                (o.ecommerceOrderItems || []).reduce((os, i) => {
                                    const prod = products.find((p) => p.id === i.productId);
                                    return os + ((prod === null || prod === void 0 ? void 0 : prod.price) || 0) * (i.quantity || 0);
                                }, 0));
                        }
                        else if (key === "unitsSold") {
                            return (sum +
                                (o.ecommerceOrderItems || []).reduce((os, i) => os + (i.quantity || 0), 0));
                        }
                        return sum;
                    }, 0);
                }
                else if (key === "orders") {
                    value = orders.filter((o) => {
                        if (!o.createdAt)
                            return false;
                        const d = new Date(o.createdAt);
                        if (dateUnit === "day")
                            return (d.getFullYear() === cursor.getFullYear() &&
                                d.getMonth() === cursor.getMonth() &&
                                d.getDate() === cursor.getDate());
                        else
                            return (d.getFullYear() === cursor.getFullYear() &&
                                d.getMonth() === cursor.getMonth());
                    }).length;
                }
                else if (key === "customers") {
                    value = customers.filter((u) => {
                        if (!u.createdAt)
                            return false;
                        const d = new Date(u.createdAt);
                        if (dateUnit === "day")
                            return (d.getFullYear() === cursor.getFullYear() &&
                                d.getMonth() === cursor.getMonth() &&
                                d.getDate() === cursor.getDate());
                        else
                            return (d.getFullYear() === cursor.getFullYear() &&
                                d.getMonth() === cursor.getMonth());
                    }).length;
                }
                chartData.push(value);
                if (dateUnit === "day") {
                    cursor.setDate(cursor.getDate() + 1);
                }
                else {
                    const origDay = cursor.getDate();
                    cursor.setMonth(cursor.getMonth() + 1);
                    if (cursor.getDate() < origDay) {
                        cursor.setDate(0);
                    }
                }
                if (chartLabels.length > 400)
                    break;
            }
            return { labels: chartLabels, data: chartData };
        }
        const revenueChart = buildChartData("revenue", start, end);
        const orderValueChart = buildChartData("orders", start, end);
        const unitsSoldChart = buildChartData("unitsSold", start, end);
        const customersChart = buildChartData("customers", start, end);
        ctx === null || ctx === void 0 ? void 0 : ctx.step("Compiling dashboard summary");
        const topProducts = [...products]
            .sort((a, b) => (b.soldCount || 0) - (a.soldCount || 0))
            .slice(0, 5);
        const recentOrders = [...orders]
            .sort((a, b) => new Date(b.createdAt || 0).getTime() -
            new Date(a.createdAt || 0).getTime())
            .slice(0, 5);
        ctx === null || ctx === void 0 ? void 0 : ctx.success("Dashboard data retrieved successfully");
        return {
            totalRevenue,
            totalOrders,
            averageOrderValue,
            totalUnitsSold,
            newCustomers: newCustomersCount,
            revenueChange,
            ordersChange,
            averageOrderChange,
            unitsSoldChange,
            newCustomersChange,
            revenueChartData: revenueChart.data,
            orderValueChartData: orderValueChart.data,
            unitsSoldChartData: unitsSoldChart.data,
            customersChartData: customersChart.data,
            topProducts,
            chartData: {
                labels: revenueChart.labels,
                datasets: [
                    {
                        label: chartType,
                        data: chartType === "revenue"
                            ? revenueChart.data
                            : chartType === "orders"
                                ? orderValueChart.data
                                : chartType === "customers"
                                    ? customersChart.data
                                    : [],
                    },
                ],
            },
            pendingOrders,
            outOfStockCount,
            shippedToday,
            completedOrders: completedOrdersCount,
            recentOrders,
        };
    }
    catch (err) {
        ctx === null || ctx === void 0 ? void 0 : ctx.fail("Failed to fetch dashboard data");
        console.error("Failed to fetch ecommerce dashboard data", err);
        throw (0, error_1.createError)({
            statusCode: 500,
            message: "Failed to fetch dashboard data",
        });
    }
};
