import { Request, Response } from "express";
import { z } from "zod";
import { asyncHandler } from "../../core/asyncHandler";
import { AppError } from "../../core/AppError";
import { productService } from "./product.service";
import { PRODUCT_CUSTOMIZATION_MODES } from "ttm-shared";

/* ========================
   Schemas
======================== */

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

const querySchema = z.object({
    categoryId: z.string().uuid().optional(),
    sort: z.enum(["name", "popularity"]).optional().default("name")
});

const createProductSchema = z.object({
    name: z.string().min(1),
    price: z.number().int().positive(),
    vatRate: z.number().int().min(0),
    categoryId: z.string().uuid(),
    imageUrl: z.string().optional().nullable(),
    stationId: z.string().uuid().optional().nullable(),
    recipeId: z.string().uuid().optional().nullable(),
    configuratorId: z.string().uuid().optional().nullable(),
    logisticsRouteId: z.string().uuid().optional().nullable(),
    type: z.enum(["STANDARD", "FOOD"]).optional().default("STANDARD"),
    customizationMode: z.enum(PRODUCT_CUSTOMIZATION_MODES).optional(),
});

const updateProductSchema = createProductSchema
    .partial()
    .extend({
        active: z.boolean().optional(),
        imageUrl: z.string().optional().nullable(),
        stationId: z.string().uuid().optional().nullable(),
        recipeId: z.string().uuid().optional().nullable(),
        configuratorId: z.string().uuid().optional().nullable(),
        logisticsRouteId: z.string().uuid().optional().nullable(),
        type: z.enum(["STANDARD", "FOOD"]).optional(),
        customizationMode: z.enum(PRODUCT_CUSTOMIZATION_MODES).optional(),
    });

const availabilitySchema = z.object({
    outOfStock: z.boolean()
});

/* ========================
   Controllers
======================== */

export const getProducts = asyncHandler(
    async (req: Request, res: Response) => {
        const { categoryId, sort } = querySchema.parse(req.query);

        if (sort === "popularity") {
            const products = await productService.findAllWithPopularity(
                req.shop!.id,
                categoryId
            );
            res.json(products);
            return;
        }

        const products = await productService.findAll(
            req.shop!.id,
            { name: "asc" },
            categoryId
        );

        res.json(products);
    }
);

export const createProduct = asyncHandler(
    async (req: Request, res: Response) => {
        const data = createProductSchema.parse(req.body);

        const created = await productService.create(
            req.shop!.id,
            data,
            req.auth?.sub,
            req.senderSocketId
        );

        const product = await productService.findById(
            req.shop!.id,
            created.id
        );

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

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

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

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

        await productService.update(
            req.shop!.id,
            id,
            data,
            req.auth?.sub,
            req.senderSocketId
        );

        const updated = await productService.findById(
            req.shop!.id,
            id
        );

        res.json(updated);
    }
);

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

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

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

        await productService.softDelete(
            req.shop!.id,
            id,
            req.auth?.sub,
            req.senderSocketId
        );

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

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

        const existing = await productService.findById(req.shop!.id, id);
        if (!existing) {
            throw new AppError("Product not found", 404);
        }

        const updated = await productService.update(
            req.shop!.id,
            id,
            { outOfStock },
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(updated);
    }
);
