import { Request, Response } from "express";
import { z } from "zod";
import { ExecutionMethod, PaymentMethod } from "@prisma/client";
import { asyncHandler } from "@/core/asyncHandler";
import { AppError } from "@/core/AppError";
import { sumupService } from "./sumup.service";

const createCheckoutSchema = z.object({
    orderId: z.string().uuid(),
    amount: z.number().int().positive(),
    method: z.nativeEnum(PaymentMethod).optional().default(PaymentMethod.CARD),
    executionMethod: z.literal(ExecutionMethod.READER).optional(),
    stationId: z.string().uuid().optional(),
    shopPaymentMethodConfigId: z.string().uuid().optional(),
    stationPaymentMethodConfigId: z.string().uuid().optional(),
});

const initiateTapToPaySchema = z.object({
    orderId: z.string().uuid(),
    amount: z.number().int().positive(),
    method: z.nativeEnum(PaymentMethod).optional().default(PaymentMethod.CARD),
    stationId: z.string().uuid().optional(),
    shopPaymentMethodConfigId: z.string().uuid().optional(),
    stationPaymentMethodConfigId: z.string().uuid().optional(),
});

const activateTapToPaySchema = initiateTapToPaySchema.extend({
    requestId: z.string().uuid(),
});

const completeTapToPaySchema = z.object({
    attemptId: z.string().uuid(),
    success: z.boolean(),
    transactionCode: z.string().optional().nullable(),
    failureReason: z.string().optional().nullable(),
});

const cancelCheckoutSchema = z.object({
    attemptId: z.string().uuid(),
});

export const createSumupCheckout = asyncHandler(
    async (req: Request, res: Response) => {
        const { orderId, amount, method, executionMethod, stationId, shopPaymentMethodConfigId, stationPaymentMethodConfigId } = createCheckoutSchema.parse(req.body);

        if (!req.shop?.id) {
            throw new AppError("Missing shop context", 400);
        }

        const result = await sumupService.createCheckout(
            req.shop.id,
            orderId,
            amount,
            {
                method,
                executionMethod,
                stationId: stationId ?? null,
                shopPaymentMethodConfigId,
                stationPaymentMethodConfigId,
                senderSocketId: req.senderSocketId ?? null
            }
        );

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

export const initiateSumupTapToPay = asyncHandler(
    async (req: Request, res: Response) => {
        const { orderId, amount, method, stationId, shopPaymentMethodConfigId, stationPaymentMethodConfigId } = initiateTapToPaySchema.parse(req.body);

        if (!req.shop?.id) {
            throw new AppError("Missing shop context", 400);
        }

        const result = await sumupService.initiateTapToPay(
            req.shop.id,
            orderId,
            amount,
            {
                method,
                stationId: stationId ?? null,
                shopPaymentMethodConfigId,
                stationPaymentMethodConfigId,
                senderSocketId: req.senderSocketId ?? null
            }
        );

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

export const completeSumupTapToPay = asyncHandler(
    async (req: Request, res: Response) => {
        const { attemptId, success, transactionCode, failureReason } = completeTapToPaySchema.parse(req.body);

        if (!req.shop?.id) {
            throw new AppError("Missing shop context", 400);
        }

        const result = await sumupService.completeTapToPay(
            req.shop.id,
            attemptId,
            {
                success,
                transactionCode: transactionCode ?? null,
                failureReason: failureReason ?? null,
            },
            req.senderSocketId ?? null
        );

        res.status(200).json(result);
    }
);

export const activateSumupTapToPay = asyncHandler(
    async (req: Request, res: Response) => {
        const { requestId, orderId, amount, method, stationId, shopPaymentMethodConfigId, stationPaymentMethodConfigId } = activateTapToPaySchema.parse(req.body);

        if (!req.shop?.id) {
            throw new AppError("Missing shop context", 400);
        }

        const result = await sumupService.activateTapToPay(
            req.shop.id,
            requestId,
            orderId,
            amount,
            {
                method,
                stationId: stationId ?? null,
                shopPaymentMethodConfigId,
                stationPaymentMethodConfigId,
                senderSocketId: req.senderSocketId ?? null
            }
        );

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

export const cancelSumupCheckout = asyncHandler(
    async (req: Request, res: Response) => {
        const { attemptId } = cancelCheckoutSchema.parse(req.body);

        if (!req.shop?.id) {
            throw new AppError("Missing shop context", 400);
        }

        const result = await sumupService.cancelCheckout(
            req.shop.id,
            attemptId,
            req.senderSocketId ?? null
        );

        res.status(200).json(result);
    }
);

export const checkSumupReaderStatus = asyncHandler(
    async (req: Request, res: Response) => {
        const { deviceId } = z.object({ deviceId: z.string().uuid() }).parse(req.params);

        if (!req.shop?.id) {
            throw new AppError("Missing shop context", 400);
        }

        const result = await sumupService.checkReaderStatus(req.shop.id, deviceId);
        res.status(200).json(result);
    }
);

export const handleSumupWebhook = asyncHandler(
    async (req: Request, res: Response) => {
        if (req.query.diagnostic === "1") {
            res.status(200).json({ ok: true, diagnostic: "sumup-webhook" })
            return
        }

        if (req.query.diagnostic === "simulate") {
            const inspection = await sumupService.inspectWebhookPayload(req.body)
            res.status(200).json(inspection)
            return
        }

        console.log(`[SumUp Webhook] ▶ Received ${req.method} ${req.originalUrl}`)
        console.log(`[SumUp Webhook] Headers:`, JSON.stringify(req.headers).substring(0, 500))
        console.log(`[SumUp Webhook] Body:`, JSON.stringify(req.body).substring(0, 500))

        res.status(200).end();

        setImmediate(async () => {
            try {
                await sumupService.processWebhook(req.body);
            } catch (err) {
                console.error("Webhook error:", err);
            }
        });
    }
);