import { Request, Response } from 'express';
import prisma from '../config/database';
import { sendSuccess, sendError } from '../utils/response.utils';
import { AuthenticatedRequest } from '../middleware/auth.middleware';

export const getVariantsByBookId = async (req: Request, res: Response) => {
  try {
    const { bookId } = req.params;
    const variants = await prisma.bookVariant.findMany({
      where: { bookId, isActive: true },
      orderBy: [{ isDefault: 'desc' }, { format: 'asc' }],
    });

    return sendSuccess(res, 'Book variants retrieved successfully', variants);
  } catch (error: any) {
    return sendError(res, 'Error fetching variants', error.message, 500);
  }
};

export const getVariantById = async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const variant = await prisma.bookVariant.findUnique({
      where: { id },
      include: { book: true },
    });

    if (!variant) {
      return sendError(res, 'Variant not found', null, 404);
    }

    return sendSuccess(res, 'Variant details retrieved', variant);
  } catch (error: any) {
    return sendError(res, 'Error fetching variant', error.message, 500);
  }
};

export const createVariant = async (req: AuthenticatedRequest, res: Response) => {
  try {
    const { bookId } = req.params;
    const input = req.body;

    const book = await prisma.book.findUnique({ where: { id: bookId } });
    if (!book) {
      return sendError(res, 'Book not found', null, 404);
    }

    // Check SKU uniqueness
    const existingSku = await prisma.bookVariant.findUnique({ where: { sku: input.sku } });
    if (existingSku) {
      return sendError(res, `Variant SKU '${input.sku}' already exists`, null, 400);
    }

    // If this variant is set as default, unset other defaults for this book
    if (input.isDefault) {
      await prisma.bookVariant.updateMany({
        where: { bookId },
        data: { isDefault: false },
      });
    }

    const variant = await prisma.bookVariant.create({
      data: {
        bookId,
        title: input.title || `${book.title} (${input.format})`,
        format: input.format,
        isbn: input.isbn || null,
        sku: input.sku,
        barcode: input.barcode || null,
        price: input.price,
        discountPrice: input.discountPrice ?? null,
        stock: input.stock ?? 0,
        weight: input.weight ?? null,
        dimensions: input.dimensions ?? null,
        coverImage: input.coverImage ?? book.coverImage ?? null,
        digitalFileUrl: input.digitalFileUrl ?? null,
        digitalFormat: input.digitalFormat ?? null,
        attributes: input.attributes ?? undefined,
        isDefault: input.isDefault ?? false,
        isActive: input.isActive ?? true,
      },
    });

    // Update parent book total stock aggregate
    const totalVariantStock = await prisma.bookVariant.aggregate({
      where: { bookId, isActive: true },
      _sum: { stock: true },
    });

    if (totalVariantStock._sum.stock !== null) {
      await prisma.book.update({
        where: { id: bookId },
        data: { stock: totalVariantStock._sum.stock },
      });
      await prisma.bookInventory.upsert({
        where: { bookId },
        create: { bookId, stock: totalVariantStock._sum.stock },
        update: { stock: totalVariantStock._sum.stock },
      });
    }

    return sendSuccess(res, 'Book variant created successfully', variant, 201);
  } catch (error: any) {
    return sendError(res, 'Failed to create variant', error.message, 500);
  }
};

export const updateVariant = async (req: AuthenticatedRequest, res: Response) => {
  try {
    const { id } = req.params;
    const input = req.body;

    const existing = await prisma.bookVariant.findUnique({ where: { id } });
    if (!existing) {
      return sendError(res, 'Variant not found', null, 404);
    }

    if (input.sku && input.sku !== existing.sku) {
      const duplicateSku = await prisma.bookVariant.findUnique({ where: { sku: input.sku } });
      if (duplicateSku) {
        return sendError(res, `Variant SKU '${input.sku}' is already in use`, null, 400);
      }
    }

    if (input.isDefault) {
      await prisma.bookVariant.updateMany({
        where: { bookId: existing.bookId, id: { not: id } },
        data: { isDefault: false },
      });
    }

    const updated = await prisma.bookVariant.update({
      where: { id },
      data: {
        ...(input.title !== undefined && { title: input.title }),
        ...(input.format !== undefined && { format: input.format }),
        ...(input.isbn !== undefined && { isbn: input.isbn }),
        ...(input.sku !== undefined && { sku: input.sku }),
        ...(input.barcode !== undefined && { barcode: input.barcode }),
        ...(input.price !== undefined && { price: input.price }),
        ...(input.discountPrice !== undefined && { discountPrice: input.discountPrice }),
        ...(input.stock !== undefined && { stock: input.stock }),
        ...(input.weight !== undefined && { weight: input.weight }),
        ...(input.dimensions !== undefined && { dimensions: input.dimensions }),
        ...(input.coverImage !== undefined && { coverImage: input.coverImage }),
        ...(input.digitalFileUrl !== undefined && { digitalFileUrl: input.digitalFileUrl }),
        ...(input.digitalFormat !== undefined && { digitalFormat: input.digitalFormat }),
        ...(input.attributes !== undefined && { attributes: input.attributes }),
        ...(input.isDefault !== undefined && { isDefault: input.isDefault }),
        ...(input.isActive !== undefined && { isActive: input.isActive }),
      },
    });

    // Update parent book total stock aggregate if stock changed
    if (input.stock !== undefined) {
      const totalVariantStock = await prisma.bookVariant.aggregate({
        where: { bookId: existing.bookId, isActive: true },
        _sum: { stock: true },
      });
      if (totalVariantStock._sum.stock !== null) {
        await prisma.book.update({
          where: { id: existing.bookId },
          data: { stock: totalVariantStock._sum.stock },
        });
        await prisma.bookInventory.upsert({
          where: { bookId: existing.bookId },
          create: { bookId: existing.bookId, stock: totalVariantStock._sum.stock },
          update: { stock: totalVariantStock._sum.stock },
        });
      }
    }

    return sendSuccess(res, 'Book variant updated successfully', updated);
  } catch (error: any) {
    return sendError(res, 'Failed to update variant', error.message, 500);
  }
};

export const deleteVariant = async (req: AuthenticatedRequest, res: Response) => {
  try {
    const { id } = req.params;
    const existing = await prisma.bookVariant.findUnique({ where: { id } });
    if (!existing) {
      return sendError(res, 'Variant not found', null, 404);
    }

    await prisma.bookVariant.update({
      where: { id },
      data: { isActive: false },
    });

    return sendSuccess(res, 'Variant disabled successfully');
  } catch (error: any) {
    return sendError(res, 'Failed to delete variant', error.message, 500);
  }
};

export const updateVariantStock = async (req: AuthenticatedRequest, res: Response) => {
  try {
    const { id } = req.params;
    const { stock } = req.body;

    const variant = await prisma.bookVariant.findUnique({ where: { id } });
    if (!variant) {
      return sendError(res, 'Variant not found', null, 404);
    }

    const updated = await prisma.bookVariant.update({
      where: { id },
      data: { stock: Number(stock) },
    });

    // Update parent book stock
    const totalVariantStock = await prisma.bookVariant.aggregate({
      where: { bookId: variant.bookId, isActive: true },
      _sum: { stock: true },
    });

    if (totalVariantStock._sum.stock !== null) {
      await prisma.book.update({
        where: { id: variant.bookId },
        data: { stock: totalVariantStock._sum.stock },
      });
      await prisma.bookInventory.upsert({
        where: { bookId: variant.bookId },
        create: { bookId: variant.bookId, stock: totalVariantStock._sum.stock },
        update: { stock: totalVariantStock._sum.stock },
      });
    }

    return sendSuccess(res, `Variant stock updated to ${stock}`, updated);
  } catch (error: any) {
    return sendError(res, 'Failed to update variant stock', error.message, 500);
  }
};
