import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
import { UserStatus } from '../generated/prisma/client';
import { PrismaService } from '../infra/prisma.service';
import { AccessService } from './access.service';
import { DEFAULT_VERIFICATION_ICON, isAllowedVerificationIcon, VERIFICATION_ICONS } from './verification.constants';
import { isProtectedSystemAccount } from './system-account.util';

const HUMAN_USERS_WHERE = {
  isSystemAccount: false,
  linkedBotId: null
};

function buildBotAccountsWhere(search?: string) {
  const botFilter = {
    OR: [{ linkedBotId: { not: null } }, { isSystemAccount: true }]
  };
  if (!search) return botFilter;
  return {
    AND: [
      botFilter,
      {
        OR: [
          { displayName: { contains: search, mode: 'insensitive' as const } },
          { username: { contains: search, mode: 'insensitive' as const } },
          { linkedBot: { username: { contains: search, mode: 'insensitive' as const } } },
          { linkedBot: { name: { contains: search, mode: 'insensitive' as const } } }
        ]
      }
    ]
  };
}

@Injectable()
export class AdminService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly access: AccessService
  ) {}

  async listUsers(search?: string) {
    const users = await this.prisma.user.findMany({
      where: {
        ...HUMAN_USERS_WHERE,
        ...(search
          ? {
              OR: [
                { email: { contains: search, mode: 'insensitive' } },
                { phone: { contains: search, mode: 'insensitive' } },
                { displayName: { contains: search, mode: 'insensitive' } },
                { username: { contains: search, mode: 'insensitive' } }
              ]
            }
          : {})
      },
      orderBy: { createdAt: 'desc' },
      include: {
        userRoles: { include: { role: true } },
        userPermissions: { include: { permission: true } }
      }
    });

    return users.map((user) => this.toAdminUser(user));
  }

  async listBotAccounts(search?: string) {
    const users = await this.prisma.user.findMany({
      where: buildBotAccountsWhere(search),
      orderBy: { createdAt: 'desc' },
      include: {
        userRoles: { include: { role: true } },
        userPermissions: { include: { permission: true } },
        linkedBot: { select: { username: true, isSystemBot: true } }
      }
    });

    return users.map((user) => this.toAdminUser(user, true));
  }

  async updateUserProfile(userId: string, data: { displayName?: string; email?: string; phone?: string; backupEmail?: string; backupPhone?: string; status?: UserStatus }) {
    await this.ensureUserExists(userId);
    const user = await this.prisma.user.update({
      where: { id: userId },
      data,
      include: { userRoles: { include: { role: true } }, userPermissions: { include: { permission: true } }, linkedBot: { select: { username: true, isSystemBot: true } } }
    });
    return this.toAdminUser(user, isProtectedSystemAccount(user));
  }

  async resetPassword(userId: string, newPassword: string) {
    if (newPassword.length < 8) {
      throw new BadRequestException('Пароль должен содержать минимум 8 символов');
    }

    await this.ensureUserExists(userId);
    const user = await this.prisma.user.update({
      where: { id: userId },
      data: { passwordHash: await bcrypt.hash(newPassword, 12) },
      include: { userRoles: { include: { role: true } }, userPermissions: { include: { permission: true } }, linkedBot: { select: { username: true, isSystemBot: true } } }
    });
    return this.toAdminUser(user, isProtectedSystemAccount(user));
  }

  async suspendUser(userId: string) {
    await this.ensureUserExists(userId);
    await this.prisma.session.updateMany({ where: { userId }, data: { status: 'REVOKED' } });
    const user = await this.prisma.user.update({
      where: { id: userId },
      data: { status: UserStatus.SUSPENDED },
      include: { userRoles: { include: { role: true } }, userPermissions: { include: { permission: true } }, linkedBot: { select: { username: true, isSystemBot: true } } }
    });
    return this.toAdminUser(user, isProtectedSystemAccount(user));
  }

  async setSuperAdmin(actorUserId: string, userId: string, isSuperAdmin: boolean) {
    await this.access.assertSuperAdmin(actorUserId);
    await this.ensureUserExists(userId);

    const target = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!target) {
      throw new NotFoundException('Пользователь не найден');
    }
    if (isProtectedSystemAccount(target)) {
      throw new ForbiddenException('Нельзя назначать системные учётные записи ботов супер-администраторами');
    }

    if (target.isSuperAdmin && !isSuperAdmin) {
      const superAdmins = await this.prisma.user.count({ where: { isSuperAdmin: true, status: UserStatus.ACTIVE } });
      if (superAdmins <= 1) {
        throw new BadRequestException('Нельзя снять права у единственного супер-администратора');
      }
    }

    const user = await this.prisma.user.update({
      where: { id: userId },
      data: { isSuperAdmin },
      include: { userRoles: { include: { role: true } }, userPermissions: { include: { permission: true } }, linkedBot: { select: { username: true, isSystemBot: true } } }
    });
    return this.toAdminUser(user, isProtectedSystemAccount(user));
  }

  async setUserVerification(actorUserId: string, userId: string, data: { isVerified: boolean; verificationIcon?: string }) {
    await this.access.assertCanVerifyUsers(actorUserId);
    await this.ensureUserExists(userId);

    if (data.isVerified) {
      const icon = data.verificationIcon ?? DEFAULT_VERIFICATION_ICON;
      if (!isAllowedVerificationIcon(icon)) {
        throw new BadRequestException('Выберите значок верификации из списка');
      }
      const user = await this.prisma.user.update({
        where: { id: userId },
        data: {
          isVerified: true,
          verificationIcon: icon,
          verifiedAt: new Date()
        },
        include: { userRoles: { include: { role: true } }, userPermissions: { include: { permission: true } }, linkedBot: { select: { username: true, isSystemBot: true } } }
      });
      return this.toAdminUser(user, isProtectedSystemAccount(user));
    }

    const user = await this.prisma.user.update({
      where: { id: userId },
      data: {
        isVerified: false,
        verificationIcon: null,
        verifiedAt: null
      },
      include: { userRoles: { include: { role: true } }, userPermissions: { include: { permission: true } }, linkedBot: { select: { username: true, isSystemBot: true } } }
    });
    return this.toAdminUser(user, isProtectedSystemAccount(user));
  }

  listVerificationIcons() {
    return { icons: VERIFICATION_ICONS.map((icon) => ({ slug: icon.slug, name: icon.name })) };
  }

  async deleteUser(userId: string) {
    await this.ensureUserExists(userId);
    const user = await this.prisma.user.update({
      where: { id: userId },
      data: { status: UserStatus.DELETED, deletedAt: new Date() },
      include: { userRoles: { include: { role: true } }, userPermissions: { include: { permission: true } }, linkedBot: { select: { username: true, isSystemBot: true } } }
    });
    return this.toAdminUser(user, isProtectedSystemAccount(user));
  }

  private toAdminUser(
    user: {
      id: string;
      email: string | null;
      phone: string | null;
      backupEmail: string | null;
      backupPhone: string | null;
      displayName: string;
      username: string | null;
      isSuperAdmin: boolean;
      isVerified: boolean;
      verificationIcon: string | null;
      status: UserStatus;
      createdAt: Date;
      isSystemAccount?: boolean;
      linkedBotId?: string | null;
      userRoles?: { role: { slug: string } }[];
      userPermissions?: { permission: { slug: string } }[];
      linkedBot?: { username: string; isSystemBot: boolean } | null;
    },
    isBot = isProtectedSystemAccount(user)
  ) {
    return {
      id: user.id,
      email: user.email ?? undefined,
      phone: user.phone ?? undefined,
      backupEmail: user.backupEmail ?? undefined,
      backupPhone: user.backupPhone ?? undefined,
      displayName: user.displayName,
      username: user.username ?? undefined,
      isSuperAdmin: user.isSuperAdmin,
      isVerified: user.isVerified,
      verificationIcon: user.isVerified ? (user.verificationIcon ?? DEFAULT_VERIFICATION_ICON) : undefined,
      status: user.status,
      createdAt: user.createdAt.toISOString(),
      roles: user.userRoles?.map((link) => link.role.slug) ?? [],
      directPermissions: user.userPermissions?.map((link) => link.permission.slug) ?? [],
      isBot,
      linkedBotUsername: user.linkedBot?.username ?? undefined,
      isSystemBot: user.linkedBot?.isSystemBot ?? false
    };
  }

  private async ensureUserExists(userId: string) {
    const user = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!user) {
      throw new NotFoundException('Пользователь не найден');
    }
  }
}
