import { Request, Response } from "express";
import { z } from "zod";
import { asyncHandler } from "@/core/asyncHandler";
import { AppError } from "@/core/AppError";
import { orderService } from "../order.service";
import { OrderStatus } from "@prisma/client";
import { orderItemCustomizationSchema, ProductConfigurationSnapshotSchema, ReplaceOrderDepositLinesSchema } from "ttm-shared";

const ParamsSchema = z.object({
    orderId: z.uuid()
});

const paramsSchema = z.object({
    id: z.uuid()
});

const orderFiltersQuerySchema = z.object({
    cashSessionId: z.string().uuid().optional(),
    stationId: z.string().uuid().optional(),
});

const replaceItemsSchema = z.object({
    items: z.array(
        z.object({
            productId: z.string().uuid(),
            quantity: z.number().int().positive(),
            recipeId: z.string().uuid().optional(),
            configurationLabel: z.string().trim().max(191).optional(),
            configurationSnapshot: ProductConfigurationSnapshotSchema.optional(),
            customizations: z.array(orderItemCustomizationSchema).optional(),
        })
    )
})

const orderItemInputSchema = z.object({
    productId: z.string().uuid(),
    quantity: z.number().int().positive(),
    recipeId: z.string().uuid().optional(),
    configurationLabel: z.string().trim().max(191).optional(),
    configurationSnapshot: ProductConfigurationSnapshotSchema.optional(),
    customizations: z.array(orderItemCustomizationSchema).optional(),
})

/* ========================
   LIST ORDERS
======================== */

export const getOrdersForShop = asyncHandler(
    async (req: Request, res: Response) => {
        const filters = orderFiltersQuerySchema.parse(req.query);
        const orders = await orderService.findAll(req.shop!.id, filters);
        res.json(orders);
    }
);

/* ========================
   CREATE ORDER
======================== */

export const createOrder = asyncHandler(
    async (req: Request, res: Response) => {
        const actorId = req.auth?.sub;
        if (!actorId) {
            throw new AppError("Unauthorized", 401);
        }

        const stationId = req.body?.stationId as string | undefined;

        const order = await orderService.createDraft(
            req.shop!.id,
            actorId,
            req.senderSocketId,
            stationId
        );

        res.status(201).json(order);
    }
);

/* ========================
   GET ORDER
======================== */

export const getOrder = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const filters = orderFiltersQuerySchema.parse(req.query);

        const order = await orderService.findById(
            req.shop!.id,
            id,
            filters
        );

        if (!order) {
            throw new AppError("Order not found", 404);
        }

        res.json(order);
    }
);

/* ========================
   REPLACE ITEMS
======================== */

export const replaceItems = asyncHandler(
    async (req: Request, res: Response) => {

        const { id } = paramsSchema.parse(req.params)
        const { items } = replaceItemsSchema.parse(req.body)

        const order = await orderService.findById(
            req.shop!.id,
            id
        )

        if (!order) {
            throw new AppError("Order not found", 404)
        }

        if (order.status === OrderStatus.PAID) {
            throw new AppError("Cannot modify a paid order", 400)
        }

        const updatedOrder = await orderService.replaceItems(
            req.shop!.id,
            id,
            items,
            req.auth?.sub,
            req.senderSocketId
        )

        res.json(updatedOrder)
    }
)

export const updateOrder = asyncHandler(
    async (req: Request, res: Response) => {

        const { id } = paramsSchema.parse(req.params)
        const { status } = req.body

        if (!Object.values(OrderStatus).includes(status)) {
            throw new AppError("Invalid status", 400)
        }

        const updated = await orderService.updateStatus(
            req.shop!.id,
            id,
            status,
            req.auth?.sub,
            req.senderSocketId
        )

        res.json(updated)
    }
)

export const deleteOrder = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);

        const existing = await orderService.findById(
            req.shop!.id,
            id
        );

        if (!existing) {
            throw new AppError("Order not found", 404);
        }

        await orderService.softDelete(
            req.shop!.id,
            id
        );

        res.status(204).send();
    }
);

export const getOrdersPending = asyncHandler(
    async (req: Request, res: Response) => {

        const drafts = await orderService.getPendings(
            req.shop!.id
        );

        res.json(drafts);
    }
)

/* ========================
   FINALIZE ORDER
   Marks all non-cancelled items as DELIVERED
======================== */

export const finalizeOrder = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);

        const order = await orderService.finalizeOrder(
            req.shop!.id,
            id,
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(order);
    }
)

/* ========================
   UNFINALIZE ORDER
   Reverts DELIVERED items back to READY
======================== */

export const unfinalizeOrder = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);

        const order = await orderService.unfinalizeOrder(
            req.shop!.id,
            id,
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(order);
    }
)

export const restoreCancelledOrder = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);

        const order = await orderService.restoreCancelledOrder(
            req.shop!.id,
            id,
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(order);
    }
)

/* ========================
   ADD SINGLE ITEM (+1)
   Adds 1 unit of a product to an order (merges with existing PENDING or creates new)
======================== */

const addItemSchema = orderItemInputSchema.extend({
    quantity: z.number().int().positive().default(1),
})

export const addItem = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const { productId, quantity, recipeId, customizations } = addItemSchema.parse(req.body);

        const order = await orderService.addItems(
            req.shop!.id,
            id,
            [{ productId, quantity, recipeId, customizations }],
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(order);
    }
)

/* ========================
   DECREMENT ITEM (-1)
   Removes 1 unit of a product from an order
======================== */

const decrementItemSchema = z.object({
    productId: z.string().uuid().optional(),
    orderItemId: z.string().uuid().optional(),
}).refine(
    (value) => !!value.productId || !!value.orderItemId,
    { message: "productId or orderItemId is required" }
)

const decrementItemsBatchSchema = z.object({
    targets: z.array(z.object({
        orderItemId: z.string().uuid(),
        quantity: z.number().int().positive(),
    })).min(1)
})

export const decrementItem = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const { productId, orderItemId } = decrementItemSchema.parse(req.body);

        const order = await orderService.decrementItem(
            req.shop!.id,
            id,
            { productId, orderItemId },
            req.senderSocketId
        );

        res.json(order);
    }
)

export const decrementItemsBatch = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params)
        const { targets } = decrementItemsBatchSchema.parse(req.body)

        const order = await orderService.decrementItemsBatch(
            req.shop!.id,
            id,
            targets,
            req.senderSocketId,
        )

        res.json(order)
    }
)

/* ========================
   ADD ITEMS (batch)
   Adds multiple products to an order in one call
======================== */

const addItemsSchema = z.object({
    items: z.array(orderItemInputSchema).min(1)
})

export const addItems = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const { items } = addItemsSchema.parse(req.body);

        const order = await orderService.addItems(
            req.shop!.id,
            id,
            items,
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(order);
    }
)

export const replaceDepositLines = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params)
        const { lines } = ReplaceOrderDepositLinesSchema.parse(req.body)

        const order = await orderService.replaceDepositLines(
            req.shop!.id,
            id,
            lines,
            req.auth?.sub,
            req.senderSocketId,
        )

        res.json(order)
    }
)

/* ========================
   COMPLETE ZERO-AMOUNT ORDER
   Validates an order fully covered by promo codes (total = 0)
======================== */

export const completeZeroAmountOrder = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);

        const order = await orderService.completeZeroAmountOrder(
            req.shop!.id,
            id,
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(order);
    }
)

