import type { Request, Response } from 'express';
import asyncHandler from 'express-async-handler';
import { prisma } from '../config/db.ts';
import { toIST } from '../utils/time.ts';

const formatSequence = (sequence: any) => ({
    ...sequence,
    endedAt: sequence.endedAt ? toIST(sequence.endedAt) : null,
    createdAt: toIST(sequence.createdAt),
    updatedAt: toIST(sequence.updatedAt),
});

export const createReferenceSequence = asyncHandler(async (req: Request, res: Response) => {
    const { prefix, countFrom } = req.body;

    if (!prefix) {
        res.status(400).json({
            message: "Prefix is required",
        });
        return;
    }

    const sequenceType = "RECEIPT";
    const sequencePrefix = prefix.trim().toUpperCase();

    const allowedCountFrom = ["NEW", "CONTINUE"];

    if (!allowedCountFrom.includes(countFrom)) {
        res.status(400).json({
            message: "countFrom must be NEW or CONTINUE",
        });
        return;
    }

    // current active sequence
    const activeSequence = await prisma.referenceSequence.findFirst({
        where: {
            type: sequenceType,
            isActive: true,
            endedAt: null,
        },
        select: {
            id: true,
            prefix: true,
            lastCount: true,
        },
        orderBy: {
            createdAt: "desc",
        },
    });

    // same active prefix check
    if (
        activeSequence &&
        activeSequence.prefix === sequencePrefix
    ) {
        res.status(400).json({
            message: "This prefix is already active",
        });
        return;
    }

    // get max last count for same prefix
    const existingPrefixData =
        await prisma.referenceSequence.aggregate({
            where: {
                type: sequenceType,
                prefix: sequencePrefix,
            },
            _max: {
                lastCount: true,
            },
        });

    const maxLastCount =
        existingPrefixData._max.lastCount || 0;

    const prefixExists =
        existingPrefixData._max.lastCount !== null;

    /*
    RULES
    
    1. Existing prefix
       -> only CONTINUE allowed
    
    2. New prefix
       -> only NEW allowed
    */

    if (prefixExists && countFrom === "NEW") {
        res.status(400).json({
            message:
                "This prefix already exists. Please use CONTINUE.",
        });
        return;
    }

    if (!prefixExists && countFrom === "CONTINUE") {
        res.status(400).json({
            message:
                "This prefix does not exist before. Please use NEW.",
        });
        return;
    }

    let newLastCount = 0;

    // continue old sequence
    if (countFrom === "CONTINUE") {
        newLastCount = maxLastCount;
    }

    // new sequence
    if (countFrom === "NEW") {
        newLastCount = 0;
    }

    const newSequence = await prisma.$transaction(
        async (tx) => {
            // deactivate old sequence
            if (activeSequence) {
                await tx.referenceSequence.update({
                    where: {
                        id: activeSequence.id,
                    },
                    data: {
                        isActive: false,
                        endedAt: new Date(),
                    },
                });
            }

            // create new active sequence
            return await tx.referenceSequence.create({
                data: {
                    type: sequenceType,
                    prefix: sequencePrefix,
                    lastCount: newLastCount,
                    isActive: true,
                },
            });
        }
    );

    res.status(201).json({
        success: true,
        message: "Sequence updated successfully",
        data: newSequence,
    });
});

export const listReferenceSequences = asyncHandler(async (req: Request, res: Response) => {
    const search = (req.query.search as string)?.trim() || "";
    const status = (req.query.status as string)?.trim() || "";
    const page = Math.max(parseInt(req.query.page as string) || 1, 1);
    const limit = Math.max(parseInt(req.query.limit as string) || 10, 1);
    const skip = (page - 1) * limit;

    const where: any = {};

    if (search) {
        where.OR = [
            { type: { contains: search } },
            { prefix: { contains: search } },
        ];
    }

    if (status === "active") {
        where.isActive = true;
        where.endedAt = null;
    }

    if (status === "inactive") {
        where.isActive = false;
    }

    const [total, sequences] = await Promise.all([
        prisma.referenceSequence.count({ where }),
        prisma.referenceSequence.findMany({
            where,
            orderBy: [
                { isActive: 'desc' },
                { type: 'asc' },
                { createdAt: 'desc' },
            ],
            skip,
            take: limit,
        }),
    ]);

    res.status(200).json({
        message: "Successfully fetched reference sequences.",
        data: sequences.map(formatSequence),
        meta: {
            total,
            page,
            limit,
            totalPages: Math.ceil(total / limit),
        },
    });
});
