import {
  Injectable,
  UnauthorizedException,
  ForbiddenException,
} from '@nestjs/common';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { PasswordService } from '../../common/utils/password.service';
import { AdminTokenService } from './admin-token.service';
import { AdminAuditService } from './admin-audit.service';
import type { AdminUser } from '@prisma/client';

const MAX_FAILED_LOGINS = 5;
const LOCK_DURATION_MINUTES = 15;

@Injectable()
export class AdminAuthService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly passwordService: PasswordService,
    private readonly tokenService: AdminTokenService,
    private readonly auditService: AdminAuditService,
  ) {}

  async login(email: string, password: string, ip?: string, userAgent?: string) {
    const adminUser = await this.prisma.adminUser.findUnique({
      where: { email },
    });

    if (!adminUser || adminUser.deletedAt) {
      throw new UnauthorizedException('邮箱或密码错误');
    }

    if (adminUser.status !== 'ACTIVE') {
      throw new ForbiddenException('账号已被禁用');
    }

    if (adminUser.lockedUntil && new Date(adminUser.lockedUntil) > new Date()) {
      const remaining = Math.ceil(
        (new Date(adminUser.lockedUntil).getTime() - Date.now()) / 60000,
      );
      throw new ForbiddenException(
        `账号已被锁定，请在 ${remaining} 分钟后重试`,
      );
    }

    const valid = await this.passwordService.verify(password, adminUser.passwordHash);
    if (!valid) {
      const newCount = adminUser.failedLoginCount + 1;
      const updates: any = { failedLoginCount: newCount };

      if (newCount >= MAX_FAILED_LOGINS) {
        updates.lockedUntil = new Date(
          Date.now() + LOCK_DURATION_MINUTES * 60 * 1000,
        );
      }

      await this.prisma.adminUser.update({
        where: { id: adminUser.id },
        data: updates,
      });

      await this.auditService.log({
        adminUserId: adminUser.id,
        action: 'LOGIN_FAILED',
        ip,
        userAgent,
      });

      throw new UnauthorizedException('邮箱或密码错误');
    }

    await this.prisma.adminUser.update({
      where: { id: adminUser.id },
      data: {
        failedLoginCount: 0,
        lockedUntil: null,
        lastLoginAt: new Date(),
        lastLoginIp: ip ?? null,
      },
    });

    const { token: refreshToken, hash } = this.tokenService.generateRefreshToken();

    const session = await this.prisma.adminSession.create({
      data: {
        adminUserId: adminUser.id,
        refreshTokenHash: hash,
        ip: ip ?? null,
        userAgent: userAgent ?? null,
        expiresAt: new Date(Date.now() + 7 * 86400000),
      },
    });

    const accessToken = await this.tokenService.generateAccessToken(
      adminUser,
      session.id,
    );

    await this.auditService.log({
      adminUserId: adminUser.id,
      action: 'LOGIN',
      ip,
      userAgent,
    });

    return {
      accessToken,
      refreshToken,
      adminUser: this.serializeAdminUser(adminUser),
    };
  }

  async refresh(refreshToken: string, ip?: string, userAgent?: string) {
    const hash = this.tokenService.hashToken(refreshToken);
    const session = await this.prisma.adminSession.findFirst({
      where: { refreshTokenHash: hash, revokedAt: null },
      include: { adminUser: true },
    });

    if (!session || new Date(session.expiresAt) < new Date()) {
      throw new UnauthorizedException('刷新令牌无效或已过期');
    }

    if (
      session.adminUser.deletedAt ||
      session.adminUser.status !== 'ACTIVE'
    ) {
      throw new UnauthorizedException('账号不可用');
    }

    await this.prisma.adminSession.update({
      where: { id: session.id },
      data: { revokedAt: new Date() },
    });

    const { token: newRefreshToken, hash: newHash } =
      this.tokenService.generateRefreshToken();

    const newSession = await this.prisma.adminSession.create({
      data: {
        adminUserId: session.adminUserId,
        refreshTokenHash: newHash,
        ip: ip ?? null,
        userAgent: userAgent ?? null,
        expiresAt: new Date(Date.now() + 7 * 86400000),
      },
    });

    const accessToken = await this.tokenService.generateAccessToken(
      session.adminUser,
      newSession.id,
    );

    return {
      accessToken,
      refreshToken: newRefreshToken,
      adminUser: this.serializeAdminUser(session.adminUser),
    };
  }

  async logout(adminUserId: string, refreshToken: string) {
    const hash = this.tokenService.hashToken(refreshToken);
    const session = await this.prisma.adminSession.findFirst({
      where: {
        refreshTokenHash: hash,
        adminUserId,
        revokedAt: null,
      },
    });

    if (session) {
      await this.prisma.adminSession.update({
        where: { id: session.id },
        data: { revokedAt: new Date() },
      });
    }
  }

  async getMe(adminUserId: string) {
    const adminUser = await this.prisma.adminUser.findUnique({
      where: { id: adminUserId },
    });

    if (!adminUser || adminUser.deletedAt) {
      throw new UnauthorizedException('管理员账号不存在');
    }

    return this.serializeAdminUser(adminUser);
  }

  private serializeAdminUser(adminUser: AdminUser) {
    return {
      id: adminUser.id,
      email: adminUser.email,
      displayName: adminUser.displayName,
      role: adminUser.role,
      status: adminUser.status,
      twoFactorEnabled: adminUser.twoFactorEnabled,
      lastLoginAt: adminUser.lastLoginAt,
      lastLoginIp: adminUser.lastLoginIp,
      createdAt: adminUser.createdAt,
    };
  }
}
