import db from "@/lib/db";
import { format, min } from "date-fns";
import { Prisma } from "@prisma/client";
import {
  eachDayOfInterval,
  eachWeekOfInterval,
  eachMonthOfInterval,
  eachYearOfInterval,
  startOfDay,
  endOfDay,
  startOfWeek,
  endOfWeek,
  startOfMonth,
  endOfMonth,
  startOfYear,
  endOfYear,
  differenceInDays,
  differenceInWeeks,
  differenceInMonths,
} from "date-fns";

export const getTotalSales = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    const dateFilter: Prisma.OrdersWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }

    //console.log(dateFilter);

    const result = await db.orders.aggregate({
      _sum: { order_total_amount: true },
      _count: { id: true },
      where: dateFilter,
    });

    const totalRevenue = result._sum.order_total_amount || 0;
    const totalOrders = result._count.id || 0;

    return { totalOrders, totalRevenue };
  } catch (error) {
    console.error(error);
    return { totalOrders: 0, totalRevenue: 0 };
  }
};

export const getTotalCustomers = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {


  const dateFilter: Prisma.UserWhereInput = {};

  if (createdAfter || createdBefore) {
    dateFilter.created_on = {};
    if (createdAfter) dateFilter.created_on.gte = createdAfter;
    if (createdBefore) dateFilter.created_on.lte = createdBefore;
  }

  try {
    const totalCustomers = await db.user.count({
      where: {
        role: { not: "ADMIN" },
        ...dateFilter,
      },
    });

    return totalCustomers;
  } catch (error) {
    console.error(error);
    return 0;
  }
};

export const getNewUsersData = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  const startDate = createdAfter || new Date();
  const endDate = createdBefore || new Date();

  const dateFilter: Prisma.UserWhereInput = {
    created_on: {
      gte: startOfDay(startDate),
      lte: endOfDay(endDate),
    },
  };

  try {
    const users = await db.user.findMany({
      where: {
        role: { not: "ADMIN" },
        ...dateFilter,
      },
      orderBy: { created_on: "asc" },
    });

    const days = differenceInDays(endDate, startDate);
    let intervalArray: Date[];
    let format: (date: Date, index: number) => string;

    if (days <= 13) {
      // Less than 13 days: daily intervals with day names (Mon, Tue, etc.)
      intervalArray = eachDayOfInterval({ start: startDate, end: endDate });
      format = (date) => date.toLocaleDateString("en-US", { weekday: "short" });
    } else if (days <= 90) {
      // 13 to 90 days: weekly intervals with labels (W1, W2, ...)
      intervalArray = eachWeekOfInterval({ start: startDate, end: endDate });
      format = (_, index) => `W${index + 1}`;

    } else if (days <= 365) {
      // 91 to 365 days: monthly intervals with month names (Jan, Feb, ...)
      intervalArray = eachMonthOfInterval({ start: startDate, end: endDate });
      format = (date) => date.toLocaleDateString("en-US", { month: "short" });
    } else if (days < 365 * 2.5) {
      // 1 to 2.5 years: monthly intervals with month names and year (Jan 2024, Feb 2024, ...)
      intervalArray = eachMonthOfInterval({ start: startDate, end: endDate });
      format = new Intl.DateTimeFormat("en", { month: "short", year: "2-digit" })
        .format;
    }
    else {
      // More than 365 days: yearly intervals with year numbers (2024, 2025, ...)
      intervalArray = eachYearOfInterval({ start: startDate, end: endDate });
      format = (date) => date.toLocaleDateString("en-US", { year: "numeric" });
    }

    // Group new users by chosen interval
    const userData = intervalArray.map((date, index) => {
      let intervalStart: Date;
      let intervalEnd: Date;

      if (days <= 13) {
        intervalStart = startOfDay(date);
        intervalEnd = endOfDay(date);
      } else if (days <= 90) {
        intervalStart = startOfWeek(date);
        intervalEnd = endOfWeek(date);
      } else if (days <= 365) {
        intervalStart = startOfMonth(date);
        intervalEnd = endOfMonth(date);
      } else if (days < 365 * 2.5) {
        intervalStart = startOfMonth(date);
        intervalEnd = endOfMonth(date);
      } else {
        intervalStart = startOfYear(date);
        intervalEnd = endOfYear(date);
      }

      const newUserCount = users.filter(
        (user) =>
          user.created_on >= intervalStart && user.created_on <= intervalEnd
      ).length;

      return {
        interval: format(date, index),
        newUsers: newUserCount,
      };
    });

    //console.log(userData);
    return userData;
  } catch (error) {
    console.error(error);
    return [];
  }
};


export const getSalesData = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  const startDate = createdAfter || new Date();
  const endDate = createdBefore || new Date();

  // Define date filtering conditions
  const dateFilter: Prisma.PaymentsWhereInput = {
    created_on: {
      gte: startOfDay(startDate),
      lte: endOfDay(endDate),
    },
  };

  try {

    const payments = await db.payments.findMany({
      where: {
        payment_status: "PAID",
        ...dateFilter,
      },
      orderBy: { created_on: "asc" },
    });

    const days = differenceInDays(endDate, startDate);
    let intervalArray: Date[];
    let format: (date: Date, index: number) => string;

    if (days < 13) {
      // Less than 13 days: show day names (Mon, Tue, etc.)
      intervalArray = eachDayOfInterval({ start: startDate, end: endDate });
      format = (date) => date.toLocaleDateString("en-US", { weekday: "short" });
    } else if (days <= 90) {
      // 13 to 90 days: show week numbers (W1, W2, ...)
      intervalArray = eachWeekOfInterval({ start: startDate, end: endDate });
      format = (_, index) => `W${index + 1}`;
    } else if (days <= 365) {
      // 91 to 365 days: show month names (Jan, Feb, ...)
      intervalArray = eachMonthOfInterval({ start: startDate, end: endDate });
      format = (date) => date.toLocaleDateString("en-US", { month: "short" });
    } else if (days < 365 * 2.5) {
      intervalArray = eachMonthOfInterval({ start: startDate, end: endDate });
      format = new Intl.DateTimeFormat("en", { month: "short", year: "2-digit" })
        .format; // Month Year
    } else {
      // More than 365 days: show years
      intervalArray = eachYearOfInterval({ start: startDate, end: endDate });
      format = (date) => date.toLocaleDateString("en-US", { year: "numeric" });
    }

    // Group sales by the chosen interval
    const salesData = intervalArray.map((date, index) => {
      let intervalStart: Date;
      let intervalEnd: Date;

      if (days <= 13) {
        intervalStart = startOfDay(date);
        intervalEnd = endOfDay(date);
      } else if (days <= 90) {
        intervalStart = startOfWeek(date);
        intervalEnd = endOfWeek(date);
      } else if (days <= 365) {
        intervalStart = startOfMonth(date);
        intervalEnd = endOfMonth(date);
      } else if (days < 365 * 2.5) {
        intervalStart = startOfMonth(date);
        intervalEnd = endOfMonth(date);
      } else {
        intervalStart = startOfYear(date);
        intervalEnd = endOfYear(date);
      }

      const totalSales = payments
        .filter(
          (payment) =>
            payment.created_on >= intervalStart &&
            payment.created_on <= intervalEnd
        )
        .reduce((sum, payment) => sum + payment.amount, 0);

      return {
        interval: format(date, index),
        totalSales,
      };
    });

    return salesData;
  } catch (error) {
    console.error(error);
    return [];
  }
};


export const getOrdersPieData = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  const dateFilter: Prisma.OrdersWhereInput = {};
  if (createdAfter || createdBefore) {
    dateFilter.created_on = {};
    if (createdAfter) dateFilter.created_on.gte = createdAfter;
    if (createdBefore) dateFilter.created_on.lte = createdBefore;
  }

  try {
    const ordersCountByStatus = await db.orders.groupBy({
      by: ["order_status"],
      _count: {
        id: true,
      },
      where: dateFilter,
    });

    //console.log(ordersCountByStatus);

    const ordersPieData = [
      { name: "ADDED_TO_CART", total: 0 },
      { name: "PLACED", total: 0 },
      { name: "PROCESSING", total: 0 },
      { name: "DISPATCHED", total: 0 },
      { name: "DELIVERED", total: 0 },
      { name: "CANCELED", total: 0 },
    ];

    ordersCountByStatus.forEach((statusCount) => {
      const statusEntry = ordersPieData.find(
        (entry) => entry.name === statusCount.order_status
      );
      if (statusEntry) {
        statusEntry.total = statusCount._count.id;
      }
    });

    return ordersPieData;
  } catch (error) {
    console.error(error);
    return [];
  }
}


export const getRecentSalesData = async (
  createdAfter: Date | null = null,
  createdBefore: Date | null = null
): Promise<RecentSalesData[]> => {
  // Build date filter
  const dateFilter: Prisma.OrdersWhereInput = {};
  if (createdAfter || createdBefore) {
    dateFilter.created_on = {};
    if (createdAfter) dateFilter.created_on.gte = createdAfter;
    if (createdBefore) dateFilter.created_on.lte = createdBefore;
  }

  try {
    // Fetch orders with related data
    const orders = await db.orders.findMany({
      where: {
        ...dateFilter,
        order_status: {
          not: "ADDED_TO_CART", // Exclude items that are just in cart
          in: ["PLACED", "PROCESSING", "DISPATCHED", "DELIVERED", "CANCELED"],
        },
      },
      include: {
        order_items: {
          include: {
            variant: true,
          },
        },
      },
      orderBy: {
        created_on: "desc",
      },
    });

    // Transform the data into the required format
    const salesData: RecentSalesData[] = orders.flatMap(
      (order): RecentSalesData[] =>
        order.order_items.map(
          (item): RecentSalesData => ({
            id: item.uuid,
            product: item.variant.name,
            quantity: item.quantity,
            amount: item.variant.special_price * item.quantity,
            status: order.order_status as RecentSalesData["status"],
          })
        )
    );

    //console.log(salesData);
    return salesData;
  } catch (error) {
    console.error(error);
    return [];
  }
};



//Total Products
export const getTotalProducts = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    const dateFilter: Prisma.ProductWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }
    const totalProducts = await db.product.count(
      {
        where: {
          ...dateFilter,
        },
      }
    );
    return totalProducts;
  } catch (error) {
    console.error(error);
    return 0;
  }
};

//Total Inventory
export const getTotalInventory = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    const dateFilter: Prisma.ProductVariantWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }
    const totalInventory = await db.productVariant.aggregate({
      _sum: { quantity: true },
      where: {
        ...dateFilter,
      },
    });
    return totalInventory._sum.quantity || 0;
  } catch (error) {
    console.error(error);
    return 0;
  }
};

//Total WishList
export const getUserWishlist = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    const dateFilter: Prisma.WishlistWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }
    const totalWishlist = await db.wishlist.count(
      {
        where: {
          ...dateFilter,
        },
      }
    );
    return totalWishlist;
  } catch (error) {
    console.error(error);
    return 0;
  }
};
//Total Categories
export const getTotalCategories = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    const dateFilter: Prisma.CategoriesWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }
    const totalCategories = await db.categories.count({
      where: {
        category_type: "MAIN_CATEGORY",
        ...dateFilter,
      },
    });
    return totalCategories;
  } catch (error) {
    console.error(error);
    return 0;
  }
}
// const categoriesData = [
//   { name: "Category 1", total: 10 },
//   { name: "Category 2", total: 20 },
//   { name: "Category 3", total: 40 },
//   { name: "Category 4", total: 22 },
//   { name: "Category 5", total: 30 },
//   { name: "Category 6", total: 15 },
// ];

// Name and total count of categories

export const getCategoriesData = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    // Define the date filter
    const dateFilter: Prisma.CategoriesWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }

    // Fetch categories and count the associated products
    const categoriesData = await db.categories.findMany({
      where: dateFilter,
      select: {
        name: true,

      },
    });


    // Transform the data to get the required format
    const formattedData = categoriesData.map((category) => ({
      name: category.name,
    }))

    return formattedData;
  } catch (error) {
    console.error("Error fetching categories data:", error);
    return [];
  }
};

//Name and total count of collections

export const getCollectionsData = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    // Define the date filter
    const dateFilter: Prisma.CollectionsWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }

    // Fetch collections and count the associated products
    const collectionsData = await db.collections.findMany({
      where: dateFilter,
      select: {
        title: true,
        products: {
          select: {
            id: true, // Select the product IDs to count
          },
        },
      },
    });

    const formattedData = collectionsData.map((collection) => ({
      name: collection.title,
      total: collection.products.length,
    }));

    return formattedData;
  } catch (error) {
    console.error("Error fetching collections data:", error);
    return [];
  }
};


//Total collections
export const getTotalCollections = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    const dateFilter: Prisma.CollectionsWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }
    const totalCollections = await db.collections.count({
      where: {
        ...dateFilter,
      },
    });
    return totalCollections;
  } catch (error) {
    console.error(error);
    return 0;
  }
}

//Top 10 Selling Products

export const getTop10SoldProducts = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    const dateFilter: Prisma.OrderItemsWhereInput = {};

    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }

    // Group data by product_id
    const top10SoldProducts = await db.orderItems.groupBy({
      by: ["variant_id"],
      _count: { id: true },
      _sum: { quantity: true },
      where: dateFilter,
      orderBy: [{ _count: { id: "desc" } }],
      take: 10,
    });

    // Fetch product names for the product IDs
    const variantds = top10SoldProducts.map((variant) => variant.variant_id);
    const products = await db.productVariant.findMany({
      where: { id: { in: variantds } },
      select: { id: true, name: true },
    });

    // Create a mapping of product_id to product_name
    const productMap = products.reduce((map, product) => {
      map[product.id] = product.name;
      return map;
    }, {} as Record<number, string>);

    // Define fill colors
    const fillColors = [
      "#ff8042",
      "#ffbb28",
      "#ff7f50",
      "#ffc658",
      "#d0ed59",
      "#a4de6c",
      "#82ca9d",
      "#8dd1e1",
      "#83a6ed",
      "#8884d8",
    ];

    console.log(top10SoldProducts);
    // Map the grouped data to the desired format
    return top10SoldProducts.map((product, index) => ({
      name: productMap[product.variant_id] || "Unknown Product",
      sold: product._count.id,
      quantity: product._sum.quantity,
      fill: fillColors[index % fillColors.length], // Loop through colors
    }));
  } catch (error) {
    console.error(error);
    return [];
  }
};


//Top 10 WishList Products
export const getTop10WishProducts = async (
  createdAfter: Date | null,
  createdBefore: Date | null
) => {
  try {
    // Define date filtering conditions
    const dateFilter: Prisma.WishlistWhereInput = {};
    if (createdAfter || createdBefore) {
      dateFilter.created_on = {};
      if (createdAfter) dateFilter.created_on.gte = createdAfter;
      if (createdBefore) dateFilter.created_on.lte = createdBefore;
    }
    // Group wishlists by variant_id to get the top 10 wished products
    const topWishProducts = await db.wishListItem.groupBy({
      by: ['variant_id'],
      _count: {
        variant_id: true,
      },
      orderBy: {
        _count: {
          variant_id: 'desc',
        },
      },
      take: 10,
    });

    // Fetch product names for the variant IDs
    const variantIds = topWishProducts.map((item) => item.variant_id);
    const variants = await db.productVariant.findMany({
      where: { id: { in: variantIds } },
      select: { id: true, name: true },
    });

    // Create a mapping of variant_id to name
    const variantMap = variants.reduce((map, variant) => {
      map[variant.id] = variant.name;
      return map;
    }, {} as Record<number, string>);

    // Map the grouped data to the desired format
    const productWithUserCount = topWishProducts.map((item) => ({
      name: variantMap[item.variant_id] || "Unknown Product",
      users: item._count.variant_id,
    }));

    return productWithUserCount;
  } catch (error) {
    console.error("Error fetching top wished products:", error);
    return [];
  }
};

