import { Prisma } from '@prisma/client';
import { prisma } from '../../config/database';
import { AppError } from '../../middleware/error-handler';
import type { CreateDonationPageInput, UpdateDonationPageInput, ListDonationPagesInput } from './donation-pages.schemas';

function generateSlug(title: string): string {
  return title
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 80);
}

async function resolveSlugCollision(slug: string, excludeId?: string): Promise<string> {
  let candidate = slug;
  let suffix = 2;

  while (true) {
    const existing = await prisma.donationPage.findUnique({
      where: { slug: candidate },
      select: { id: true },
    });

    if (!existing || (excludeId && existing.id === excludeId)) {
      return candidate;
    }

    candidate = `${slug}-${suffix}`;
    suffix++;
  }
}

export const donationPagesService = {
  /** Admin: list donation pages with pagination, search, status filter */
  async findAll(filters: ListDonationPagesInput) {
    const { page, limit, search, status } = filters;
    const skip = (page - 1) * limit;

    const where: Prisma.DonationPageWhereInput = {};

    if (search) {
      where.OR = [
        { title: { contains: search, mode: 'insensitive' } },
        { description: { contains: search, mode: 'insensitive' } },
      ];
    }

    if (status) where.status = status;

    const [pages, total] = await Promise.all([
      prisma.donationPage.findMany({
        where,
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
        include: {
          _count: {
            select: {
              orders: { where: { status: 'COMPLETED' } },
            },
          },
        },
      }),
      prisma.donationPage.count({ where }),
    ]);

    // Compute totalRaised for each page
    const pagesWithStats = await Promise.all(
      pages.map(async (p) => {
        const agg = await prisma.order.aggregate({
          where: { donationPageId: p.id, status: 'COMPLETED' },
          _sum: { amountCAD: true },
        });
        return {
          ...p,
          totalRaised: agg._sum.amountCAD || 0,
        };
      }),
    );

    return {
      pages: pagesWithStats,
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  /** Admin: get by ID with stats */
  async findById(id: string) {
    const page = await prisma.donationPage.findUnique({
      where: { id },
      include: {
        _count: {
          select: {
            orders: { where: { status: 'COMPLETED' } },
          },
        },
      },
    });

    if (!page) {
      throw new AppError(404, 'Donation page not found', 'DONATION_PAGE_NOT_FOUND');
    }

    const agg = await prisma.order.aggregate({
      where: { donationPageId: id, status: 'COMPLETED' },
      _sum: { amountCAD: true },
    });

    return { ...page, totalRaised: agg._sum.amountCAD || 0 };
  },

  /** Create donation page with auto-generated slug */
  async create(data: CreateDonationPageInput, userId: string) {
    const baseSlug = generateSlug(data.title);
    const slug = await resolveSlugCollision(baseSlug);

    // Enforce highlight exclusivity
    if (data.highlightPage) {
      await prisma.donationPage.updateMany({
        where: { highlightPage: true },
        data: { highlightPage: false },
      });
    }

    const page = await prisma.donationPage.create({
      data: {
        slug,
        title: data.title,
        description: data.description ?? null,
        status: data.status || 'DRAFT',
        suggestedAmounts: data.suggestedAmounts ?? [1000, 2500, 5000, 10000],
        minimumAmount: data.minimumAmount ?? 500,
        thankYouMessage: data.thankYouMessage ?? 'Thank you for your support!',
        coverPhoto: data.coverPhoto ?? null,
        coverVideoId: data.coverVideoId ?? null,
        highlightPage: data.highlightPage ?? false,
        showDonorCount: data.showDonorCount ?? true,
        showTotalRaised: data.showTotalRaised ?? false,
        goalAmount: data.goalAmount ?? null,
        createdByUserId: userId,
      },
    });

    return page;
  },

  /** Update donation page, regenerate slug if title changes */
  async update(id: string, data: UpdateDonationPageInput) {
    const existing = await prisma.donationPage.findUnique({ where: { id } });
    if (!existing) {
      throw new AppError(404, 'Donation page not found', 'DONATION_PAGE_NOT_FOUND');
    }

    const updateData: Prisma.DonationPageUncheckedUpdateInput = { ...data };

    // Regenerate slug if title changes
    if (data.title && data.title !== existing.title) {
      const baseSlug = generateSlug(data.title);
      updateData.slug = await resolveSlugCollision(baseSlug, id);
    }

    // Enforce highlight exclusivity
    if (data.highlightPage) {
      await prisma.donationPage.updateMany({
        where: { highlightPage: true, id: { not: id } },
        data: { highlightPage: false },
      });
    }

    return prisma.donationPage.update({
      where: { id },
      data: updateData,
    });
  },

  /** Delete donation page */
  async delete(id: string) {
    const existing = await prisma.donationPage.findUnique({
      where: { id },
      include: { _count: { select: { orders: { where: { status: 'COMPLETED' } } } } },
    });
    if (!existing) {
      throw new AppError(404, 'Donation page not found', 'DONATION_PAGE_NOT_FOUND');
    }

    // Nullify order references before deleting
    await prisma.order.updateMany({
      where: { donationPageId: id },
      data: { donationPageId: null },
    });

    await prisma.donationPage.delete({ where: { id } });
  },

  /** Public: list active donation pages with stats */
  async findActivePages(limit: number = 20) {
    const pages = await prisma.donationPage.findMany({
      where: { status: 'ACTIVE' },
      orderBy: [
        { highlightPage: 'desc' },
        { createdAt: 'desc' },
      ],
      take: limit,
    });

    return Promise.all(
      pages.map(async (p) => {
        const [agg, donorCount] = await Promise.all([
          prisma.order.aggregate({
            where: { donationPageId: p.id, status: 'COMPLETED' },
            _sum: { amountCAD: true },
          }),
          prisma.order.count({
            where: { donationPageId: p.id, status: 'COMPLETED' },
          }),
        ]);
        return {
          ...p,
          totalRaised: agg._sum.amountCAD || 0,
          donorCount,
        };
      }),
    );
  },

  /** Public: get active page by slug, 404 if not ACTIVE */
  async findBySlugPublic(slug: string) {
    const page = await prisma.donationPage.findUnique({
      where: { slug },
    });

    if (!page) {
      throw new AppError(404, 'Donation page not found', 'DONATION_PAGE_NOT_FOUND');
    }

    if (page.status !== 'ACTIVE') {
      throw new AppError(404, 'Donation page not found', 'DONATION_PAGE_NOT_FOUND');
    }

    const [agg, donorCount] = await Promise.all([
      prisma.order.aggregate({
        where: { donationPageId: page.id, status: 'COMPLETED' },
        _sum: { amountCAD: true },
      }),
      prisma.order.count({
        where: { donationPageId: page.id, status: 'COMPLETED' },
      }),
    ]);

    return {
      ...page,
      totalRaised: agg._sum.amountCAD || 0,
      donorCount,
    };
  },

  /** Admin: get aggregate stats for a page */
  async getPageStats(pageId: string) {
    const [totalDonations, totalAmount, uniqueDonors, avgDonation] = await Promise.all([
      prisma.order.count({
        where: { donationPageId: pageId, status: 'COMPLETED' },
      }),
      prisma.order.aggregate({
        where: { donationPageId: pageId, status: 'COMPLETED' },
        _sum: { amountCAD: true },
      }),
      prisma.order.groupBy({
        by: ['buyerEmail'],
        where: { donationPageId: pageId, status: 'COMPLETED' },
      }),
      prisma.order.aggregate({
        where: { donationPageId: pageId, status: 'COMPLETED' },
        _avg: { amountCAD: true },
      }),
    ]);

    return {
      totalDonations,
      totalAmount: totalAmount._sum.amountCAD || 0,
      uniqueDonors: uniqueDonors.length,
      averageDonation: avgDonation._avg.amountCAD || 0,
    };
  },
};
