import { Request, Response } from "express";
import { z } from "zod";
import { asyncHandler } from "../../core/asyncHandler";
import { AppError } from "../../core/AppError";
import { stationService } from "./station.service";
import { stationStockService } from "./station-stock.service";
import { CreateStationSchema, ReplaceStationProductDepositLinksSchema, SetStationProductsSchema, SetStationShopUsersSchema, UpdateStationProductionWorkflowSchema, UpdateStationSchema } from "ttm-shared";

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

const stationProductParamsSchema = z.object({
    id: z.string().uuid(),
    productId: z.string().uuid(),
});

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

        const station = await stationService.findById(req.shop!.id, id)

        if (!station) {
            throw new AppError("Station not found", 404)
        }

        res.json(station)
    }
)

export const setStationShopUsers = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params)
        const input = SetStationShopUsersSchema.parse(req.body)

        const updated = await stationService.setShopUsers(
            req.shop!.id,
            id,
            input,
            req.auth?.sub,
            req.senderSocketId
        )

        res.json(updated)
    }
)

export const setStationProducts = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params)
        const input = SetStationProductsSchema.parse(req.body)

        const updated = await stationService.setStationProducts(
            req.shop!.id,
            id,
            input,
            req.auth?.sub,
            req.senderSocketId
        )

        res.json(updated)
    }
)

export const getStationProductDepositLinks = asyncHandler(
    async (req: Request, res: Response) => {
        const { id, productId } = stationProductParamsSchema.parse(req.params)

        const links = await stationService.getStationProductDepositLinks(
            req.shop!.id,
            id,
            productId,
        )

        res.json(links)
    }
)

export const replaceStationProductDepositLinks = asyncHandler(
    async (req: Request, res: Response) => {
        const { id, productId } = stationProductParamsSchema.parse(req.params)
        const input = ReplaceStationProductDepositLinksSchema.parse(req.body)

        const updated = await stationService.replaceStationProductDepositLinks(
            req.shop!.id,
            id,
            productId,
            input,
            req.auth?.sub,
            req.senderSocketId,
        )

        res.json(updated)
    }
)

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

        const station = await stationService.findById(
            req.shop!.id,
            id
        );

        if (!station) {
            throw new AppError("Station not found", 404);
        }

        const queue = await stationService.getQueue(
            req.shop!.id,
            id
        );

        res.json(queue);
    }
);

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

        const station = await stationService.findById(
            req.shop!.id,
            id
        );

        if (!station) {
            throw new AppError("Station not found", 404);
        }

        if (station.mainType !== "PRODUCTION") {
            throw new AppError("Station is not a production station", 400);
        }

        const queue = await stationService.getProductionQueue(
            req.shop!.id,
            id
        );

        res.json(queue);
    }
);

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

        const stats = await stationService.getProductionStationStats(
            req.shop!.id,
            id,
        )

        res.json(stats)
    }
)

export const getStationProductionWorkflow = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params)
        const workflow = await stationService.getProductionWorkflow(req.shop!.id, id)
        res.json(workflow)
    }
)

export const updateStationProductionWorkflow = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params)
        const input = UpdateStationProductionWorkflowSchema.parse(req.body)
        const updated = await stationService.updateProductionWorkflow(
            req.shop!.id,
            id,
            input,
            req.auth?.sub,
            req.senderSocketId,
        )

        res.json(updated)
    }
)

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

        const stats = await stationService.getStationStats(
            req.shop!.id,
            id
        );

        res.json(stats);
    }
);

export const getStations = asyncHandler(
    async (req: Request, res: Response) => {
        const stations = await stationService.findAll(req.shop!.id);
        res.json(stations);
    }
);

export const createStation = asyncHandler(
    async (req: Request, res: Response) => {
        const input = CreateStationSchema.parse(req.body);

        const station = await stationService.create(
            req.shop!.id,
            input,
            req.auth?.sub,
            req.senderSocketId
        );

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

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

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

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

        const updated = await stationService.update(
            req.shop!.id,
            id,
            data,
            req.auth?.sub,
            req.senderSocketId
        );

        res.json(updated);
    }
);

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

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

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

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

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

/* ========================
   STATION STOCK
======================== */

export const getStationStock = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const stock = await stationStockService.getStock(req.shop!.id, id);
        res.json(stock);
    }
);

const sendToReserveBody = z.object({
    orderItemId: z.string().uuid()
});

export const postSendToReserve = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const { orderItemId } = sendToReserveBody.parse(req.body);
        const userId = req.auth?.sub;
        if (!userId) throw new AppError("Unauthorized", 401);

        const result = await stationStockService.sendToReserve(
            req.shop!.id, id, orderItemId, userId, req.senderSocketId
        );
        res.json(result);
    }
);

const useFromStockBody = z.object({
    orderItemId: z.string().uuid()
});

export const postUseFromStock = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const { orderItemId } = useFromStockBody.parse(req.body);
        const userId = req.auth?.sub;
        if (!userId) throw new AppError("Unauthorized", 401);

        const result = await stationStockService.useFromStock(
            req.shop!.id, id, orderItemId, userId, req.senderSocketId
        );
        res.json(result);
    }
);

const stockBody = z.object({
    productId: z.string().uuid(),
    quantity: z.number().int().positive()
});

export const postIncrementStock = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const { productId, quantity } = stockBody.parse(req.body);
        const userId = req.auth?.sub;
        if (!userId) throw new AppError("Unauthorized", 401);

        const result = await stationStockService.incrementStock(
            req.shop!.id, id, productId, quantity, userId, req.senderSocketId
        );
        res.json(result);
    }
);

export const postDecrementStock = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const { productId, quantity } = stockBody.parse(req.body);
        const userId = req.auth?.sub;
        if (!userId) throw new AppError("Unauthorized", 401);

        const result = await stationStockService.decrementStock(
            req.shop!.id, id, productId, quantity, userId, req.senderSocketId
        );
        res.json(result);
    }
);

export const getStationProducts = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const products = await stationService.getStationProducts(req.shop!.id, id);
        res.json(products);
    }
);

export const getRunnerQueue = asyncHandler(
    async (req: Request, res: Response) => {
        const shopId = req.shop!.id;
        const userId = req.auth?.sub;
        if (!userId) throw new AppError("Unauthorized", 401);

        const queue = await stationService.getRunnerQueue(shopId, userId);
        res.json(queue);
    }
);

export const getHasRunner = asyncHandler(
    async (req: Request, res: Response) => {
        const { id } = paramsSchema.parse(req.params);
        const hasRunner = await stationService.hasRunnerAssigned(req.shop!.id, id);
        res.json({ hasRunner });
    }
);

