import { Injectable, ConflictException, BadRequestException } from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
import { PrismaService } from '../../prisma/prisma.service';

const ADMIN_USER_FIELDS = {
  id: true,
  name: true,
  email: true,
  phone: true,
  profilePicture: true,
  role: true,
  status: true,
  createdAt: true,
  membership: { select: { type: true, validUntil: true } },
};

@Injectable()
export class AdminService {
  constructor(private prisma: PrismaService) {}

  async getStats() {
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

    const [pending, approved, suspended, premiumMembers, totalGroups, totalPosts, recentUsers] =
      await Promise.all([
        this.prisma.user.count({ where: { status: 'PENDING' } }),
        this.prisma.user.count({ where: { status: 'APPROVED' } }),
        this.prisma.user.count({ where: { status: 'SUSPENDED' } }),
        this.prisma.membership.count({ where: { type: 'PREMIUM' } }),
        this.prisma.group.count(),
        this.prisma.feed.count(),
        this.prisma.user.findMany({
          where: { createdAt: { gte: thirtyDaysAgo } },
          select: { createdAt: true },
          orderBy: { createdAt: 'asc' },
        }),
      ]);

    // Build last-30-days registration series
    const dayMap: Record<string, number> = {};
    for (let i = 29; i >= 0; i--) {
      const d = new Date(Date.now() - i * 24 * 60 * 60 * 1000);
      dayMap[d.toISOString().split('T')[0]] = 0;
    }
    recentUsers.forEach((u) => {
      const key = u.createdAt.toISOString().split('T')[0];
      if (key in dayMap) dayMap[key]++;
    });
    const registrationsByDay = Object.entries(dayMap).map(([date, count]) => ({ date, count }));

    return {
      totalUsers: pending + approved + suspended,
      pendingUsers: pending,
      approvedUsers: approved,
      suspendedUsers: suspended,
      premiumMembers,
      totalGroups,
      totalPosts,
      usersByStatus: [
        { status: 'Pending', count: pending },
        { status: 'Active', count: approved },
        { status: 'Suspended', count: suspended },
      ],
      registrationsByDay,
    };
  }

  async findAllUsers(query: { status?: string; search?: string; page?: any; limit?: any }) {
    const { status, search } = query;
    const page = Math.max(1, parseInt(query.page) || 1);
    const limit = Math.min(100, parseInt(query.limit) || 20);

    const where: any = { status: { not: 'DELETED' } };
    if (status && status !== 'ALL') where.status = status;
    if (search) {
      where.OR = [
        { name: { contains: search } },
        { email: { contains: search } },
        { phone: { contains: search } },
      ];
    }

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        where,
        select: ADMIN_USER_FIELDS,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.user.count({ where }),
    ]);

    return { users, total, page, limit };
  }

  async createUser(dto: {
    name: string;
    email?: string;
    phone?: string;
    password?: string;
    role?: string;
    membershipType?: string;
    status?: string;
  }) {
    if (!dto.email && !dto.phone) {
      throw new BadRequestException('Either email or phone is required');
    }

    const existing = await this.prisma.user.findFirst({
      where: {
        OR: [
          dto.email ? { email: dto.email } : undefined,
          dto.phone ? { phone: dto.phone } : undefined,
        ].filter(Boolean) as any[],
      },
    });
    if (existing) throw new ConflictException('A user with this email or phone already exists');

    const passwordHash = dto.password ? await bcrypt.hash(dto.password, 12) : null;

    const user = await this.prisma.user.create({
      data: {
        name: dto.name,
        email: dto.email || null,
        phone: dto.phone || null,
        passwordHash,
        role: (dto.role as any) ?? 'FREE_MEMBER',
        status: (dto.status as any) ?? 'APPROVED',
        membership: {
          create: { type: (dto.membershipType as any) ?? 'FREE' },
        },
      },
      select: ADMIN_USER_FIELDS,
    });

    return user;
  }

  async findAllGroups(query: { search?: string; type?: string; page?: any; limit?: any }) {
    const { search, type } = query;
    const page = Math.max(1, parseInt(query.page) || 1);
    const limit = Math.min(100, parseInt(query.limit) || 20);

    const where: any = {};
    if (type && type !== 'ALL') where.type = type;
    if (search) where.name = { contains: search };

    const [groups, total] = await Promise.all([
      this.prisma.group.findMany({
        where,
        select: {
          id: true,
          name: true,
          description: true,
          type: true,
          createdAt: true,
          _count: { select: { members: true, messages: true } },
        },
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.group.count({ where }),
    ]);

    return { groups, total, page, limit };
  }

  async updateUser(
    id: string,
    dto: { role?: string; status?: string; membershipType?: string; name?: string; email?: string; phone?: string; password?: string },
  ) {
    const updateData: any = {};
    if (dto.role !== undefined) updateData.role = dto.role;
    if (dto.status !== undefined) updateData.status = dto.status;
    if (dto.name !== undefined) updateData.name = dto.name;
    if (dto.email !== undefined) updateData.email = dto.email || null;
    if (dto.phone !== undefined) updateData.phone = dto.phone || null;
    if (dto.password) updateData.passwordHash = await bcrypt.hash(dto.password, 12);

    const user = await this.prisma.user.update({
      where: { id },
      data: updateData,
      select: ADMIN_USER_FIELDS,
    });

    if (dto.membershipType) {
      await this.prisma.membership.upsert({
        where: { userId: id },
        update: { type: dto.membershipType as any },
        create: { userId: id, type: dto.membershipType as any },
      });
    }

    return user;
  }
}
