import { fetchGeoInfo } from '@/lib/geoip';
import { db } from '@/lib/prisma';

type AdminUser = {
  id: string;
  name: string;
  email: string;
  role: string;
  banned: boolean;
  bannedAt: string | null;
  banReason: string | null;
  createdAt: string;
  complexCount: number;
  activeSessions: number;
};

export async function listAdminUsers(search?: string): Promise<AdminUser[]> {
  const users = await db.user.findMany({
    where: search
      ? {
          OR: [
            { name: { contains: search, mode: 'insensitive' } },
            { email: { contains: search, mode: 'insensitive' } },
          ],
        }
      : undefined,
    include: {
      _count: {
        select: { complexes: true, sessions: true },
      },
    },
    orderBy: { createdAt: 'desc' },
  });

  return users.map((user) => ({
    id: user.id,
    name: user.name,
    email: user.email,
    role: user.role,
    banned: user.banned ?? false,
    bannedAt: user.bannedAt?.toISOString() ?? null,
    banReason: user.banReason ?? null,
    createdAt: user.createdAt.toISOString(),
    complexCount: user._count.complexes,
    activeSessions: user._count.sessions,
  }));
}

type AdminUserSession = {
  id: string;
  createdAt: string;
  expiresAt: string;
  ipAddress: string | null;
  userAgent: string | null;
  city: string | null;
  country: string | null;
  countryCode: string | null;
};

export async function getUserSessions(userId: string): Promise<AdminUserSession[]> {
  const sessions = await db.session.findMany({
    where: { userId },
    orderBy: { createdAt: 'desc' },
  });

  const uniqueIps = [...new Set(sessions.map((s) => s.ipAddress).filter(Boolean))] as string[];
  const geoResults = await Promise.all(uniqueIps.map((ip) => fetchGeoInfo(ip)));
  const geoMap = new Map<
    string,
    { city: string | null; country: string | null; countryCode: string | null }
  >();
  for (let i = 0; i < uniqueIps.length; i++) {
    const info = geoResults[i];
    geoMap.set(uniqueIps[i], {
      city: info?.city ?? null,
      country: info?.country ?? null,
      countryCode: info?.countryCode ?? null,
    });
  }

  return sessions.map((s) => {
    const geo = s.ipAddress ? geoMap.get(s.ipAddress) : null;
    return {
      id: s.id,
      createdAt: s.createdAt.toISOString(),
      expiresAt: s.expiresAt.toISOString(),
      ipAddress: s.ipAddress,
      userAgent: s.userAgent,
      city: geo?.city ?? null,
      country: geo?.country ?? null,
      countryCode: geo?.countryCode ?? null,
    };
  });
}

export class AdminServiceError extends Error {
  status: 400 | 403 | 404;
  constructor(message: string, status: 400 | 403 | 404 = 400) {
    super(message);
    this.status = status;
    this.name = 'AdminServiceError';
  }
}

export async function blockUser(userId: string, banReason?: string): Promise<void> {
  const target = await db.user.findUnique({
    where: { id: userId },
    select: { role: true },
  });

  if (!target) {
    throw new AdminServiceError('Usuario no encontrado.', 404);
  }

  if (target.role === 'super_admin') {
    throw new AdminServiceError('No se puede bloquear un super_admin.', 403);
  }

  await db.user.update({
    where: { id: userId },
    data: {
      banned: true,
      bannedAt: new Date(),
      banReason: banReason ?? null,
    },
  });
}

export async function unblockUser(userId: string): Promise<void> {
  await db.user.update({
    where: { id: userId },
    data: {
      banned: false,
      bannedAt: null,
      banReason: null,
    },
  });
}

export async function revokeAllUserSessions(userId: string): Promise<number> {
  const result = await db.session.deleteMany({
    where: { userId },
  });

  return result.count;
}
