import { Injectable } from '@nestjs/common';
import { type Prisma, type UserRole } from '@prisma/client';
import { type PrismaService } from '@modules/shared/infrastructure/prisma.service';
import {
  type IAdminQueryRepository,
  type ModerationQueueResult,
  type DashboardStats,
  type RevenueStatsItem,
  type UserListResult,
  type UserDetail,
  type KycQueueResult,
} from '../../domain/repositories/admin-query.repository';

@Injectable()
export class PrismaAdminQueryRepository implements IAdminQueryRepository {
  constructor(private readonly prisma: PrismaService) {}

  async getModerationQueue(page: number, limit: number): Promise<ModerationQueueResult> {
    const skip = (page - 1) * limit;

    const [listings, total] = await Promise.all([
      this.prisma.listing.findMany({
        where: { status: 'PENDING_REVIEW' },
        include: {
          property: { select: { title: true, propertyType: true } },
          seller: { select: { fullName: true } },
        },
        orderBy: { createdAt: 'asc' },
        skip,
        take: limit,
      }),
      this.prisma.listing.count({ where: { status: 'PENDING_REVIEW' } }),
    ]);

    return {
      data: listings.map((l) => ({
        listingId: l.id,
        propertyTitle: l.property.title,
        propertyType: l.property.propertyType,
        transactionType: l.transactionType,
        priceVND: l.priceVND,
        sellerName: l.seller.fullName,
        sellerId: l.sellerId,
        moderationScore: l.moderationScore,
        createdAt: l.createdAt,
      })),
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async getDashboardStats(): Promise<DashboardStats> {
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    const [
      totalUsers,
      totalListings,
      activeListings,
      pendingModerationCount,
      totalAgents,
      verifiedAgents,
      totalTransactions,
      newUsersLast30Days,
      newListingsLast30Days,
    ] = await Promise.all([
      this.prisma.user.count(),
      this.prisma.listing.count(),
      this.prisma.listing.count({ where: { status: 'ACTIVE' } }),
      this.prisma.listing.count({ where: { status: 'PENDING_REVIEW' } }),
      this.prisma.agent.count(),
      this.prisma.agent.count({ where: { isVerified: true } }),
      this.prisma.transaction.count(),
      this.prisma.user.count({ where: { createdAt: { gte: thirtyDaysAgo } } }),
      this.prisma.listing.count({ where: { createdAt: { gte: thirtyDaysAgo } } }),
    ]);

    return {
      totalUsers,
      totalListings,
      activeListings,
      pendingModerationCount,
      totalAgents,
      verifiedAgents,
      totalTransactions,
      newUsersLast30Days,
      newListingsLast30Days,
    };
  }

  async getRevenueStats(
    startDate: Date,
    endDate: Date,
    groupBy: 'day' | 'month',
  ): Promise<RevenueStatsItem[]> {
    const payments = await this.prisma.payment.findMany({
      where: {
        status: 'COMPLETED',
        createdAt: { gte: startDate, lte: endDate },
      },
      select: {
        type: true,
        amountVND: true,
        createdAt: true,
      },
      orderBy: { createdAt: 'asc' },
    });

    const grouped = new Map<string, {
      totalRevenue: bigint;
      subscriptionRevenue: bigint;
      listingFeeRevenue: bigint;
      featuredListingRevenue: bigint;
      transactionCount: number;
    }>();

    for (const payment of payments) {
      const period = groupBy === 'day'
        ? payment.createdAt.toISOString().slice(0, 10)
        : payment.createdAt.toISOString().slice(0, 7);

      if (!grouped.has(period)) {
        grouped.set(period, {
          totalRevenue: 0n,
          subscriptionRevenue: 0n,
          listingFeeRevenue: 0n,
          featuredListingRevenue: 0n,
          transactionCount: 0,
        });
      }

      const stats = grouped.get(period)!;
      stats.totalRevenue += payment.amountVND;
      stats.transactionCount++;

      switch (payment.type) {
        case 'SUBSCRIPTION':
          stats.subscriptionRevenue += payment.amountVND;
          break;
        case 'LISTING_FEE':
          stats.listingFeeRevenue += payment.amountVND;
          break;
        case 'FEATURED_LISTING':
          stats.featuredListingRevenue += payment.amountVND;
          break;
      }
    }

    return Array.from(grouped.entries()).map(([period, stats]) => ({
      period,
      ...stats,
    }));
  }

  async getUsers(params: {
    page: number;
    limit: number;
    role?: string;
    isActive?: boolean;
    search?: string;
  }): Promise<UserListResult> {
    const { page, limit, role, isActive, search } = params;
    const skip = (page - 1) * limit;

    const where: Prisma.UserWhereInput = {};
    if (role) where.role = role as UserRole;
    if (isActive !== undefined) where.isActive = isActive;
    if (search) {
      where.OR = [
        { fullName: { contains: search, mode: 'insensitive' } },
        { email: { contains: search, mode: 'insensitive' } },
        { phone: { contains: search } },
      ];
    }

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        where,
        select: {
          id: true,
          email: true,
          phone: true,
          fullName: true,
          role: true,
          kycStatus: true,
          isActive: true,
          createdAt: true,
        },
        orderBy: { createdAt: 'desc' },
        skip,
        take: limit,
      }),
      this.prisma.user.count({ where }),
    ]);

    return {
      data: users,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async getUserDetail(userId: string): Promise<UserDetail | null> {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      include: {
        subscription: {
          include: { plan: { select: { tier: true } } },
        },
        listings: {
          select: { id: true, status: true },
          take: 100,
          orderBy: { createdAt: 'desc' },
        },
      },
    });

    if (!user) return null;

    const [transactionsCount, recentListings] = await Promise.all([
      this.prisma.transaction.count({
        where: { buyerId: userId },
      }),
      this.prisma.listing.findMany({
        where: { sellerId: userId },
        select: {
          id: true,
          status: true,
          createdAt: true,
          property: { select: { title: true } },
        },
        orderBy: { createdAt: 'desc' },
        take: 10,
      }),
    ]);

    const recentActivity = recentListings.map((l) => ({
      type: 'listing',
      description: `${l.property.title} — ${l.status}`,
      createdAt: l.createdAt,
    }));

    return {
      id: user.id,
      email: user.email,
      phone: user.phone,
      fullName: user.fullName,
      avatarUrl: user.avatarUrl,
      role: user.role,
      kycStatus: user.kycStatus,
      kycData: user.kycData,
      isActive: user.isActive,
      createdAt: user.createdAt,
      updatedAt: user.updatedAt,
      listingsCount: user.listings.length,
      activeListingsCount: user.listings.filter((l) => l.status === 'ACTIVE').length,
      transactionsCount,
      subscription: user.subscription
        ? {
            planTier: user.subscription.plan.tier,
            status: user.subscription.status,
            currentPeriodEnd: user.subscription.currentPeriodEnd,
          }
        : null,
      recentActivity,
    };
  }

  async getKycQueue(page: number, limit: number): Promise<KycQueueResult> {
    const skip = (page - 1) * limit;

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        where: { kycStatus: 'PENDING' },
        select: {
          id: true,
          fullName: true,
          email: true,
          phone: true,
          role: true,
          kycStatus: true,
          kycData: true,
          createdAt: true,
        },
        orderBy: { createdAt: 'asc' },
        skip,
        take: limit,
      }),
      this.prisma.user.count({ where: { kycStatus: 'PENDING' } }),
    ]);

    return {
      data: users.map((u) => ({
        userId: u.id,
        fullName: u.fullName,
        email: u.email,
        phone: u.phone,
        role: u.role,
        kycStatus: u.kycStatus,
        kycData: u.kycData,
        createdAt: u.createdAt,
      })),
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }
}
