import {
  Injectable,
  UnauthorizedException,
  NotFoundException,
  BadRequestException,
  Logger,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { Admin } from '@/entities/admin.entity';
import {
  AdminLoginDto,
  CreateAdminDto,
  UpdateAdminDto,
  ChangeAdminPasswordDto,
} from './dto/auth.dto';

@Injectable()
export class AdminAuthService {
  private readonly logger = new Logger(AdminAuthService.name);

  constructor(
    @InjectRepository(Admin)
    private adminRepo: Repository<Admin>,
    private jwtService: JwtService,
    private configService: ConfigService,
  ) {}

  async login(dto: AdminLoginDto) {
    const admin = await this.adminRepo
      .createQueryBuilder('admin')
      .where('admin.username = :username OR admin.phone = :phone', {
        username: dto.username,
        phone: dto.username,
      })
      .addSelect('admin.password')
      .getOne();

    if (!admin || !admin.password) {
      throw new UnauthorizedException('用户名或密码错误');
    }

    const isMatch = await bcrypt.compare(dto.password, admin.password);
    if (!isMatch) {
      throw new UnauthorizedException('用户名或密码错误');
    }

    if (admin.status === 'frozen') {
      throw new UnauthorizedException('账号已被冻结');
    }

    await this.adminRepo.update(admin.id, {
      lastLoginAt: new Date(),
    });

    return this.generateToken(admin);
  }

  async createAdmin(dto: CreateAdminDto) {
    const existing = await this.adminRepo.findOne({
      where: { username: dto.username },
    });
    if (existing) {
      throw new BadRequestException('用户名已存在');
    }

    const hashedPassword = await bcrypt.hash(dto.password, 10);
    const adminData = {
      username: dto.username,
      password: hashedPassword,
      name: dto.name,
      phone: dto.phone || null,
      email: dto.email || null,
    };
    const admin = this.adminRepo.create(adminData as any) as unknown as Admin;

    await this.adminRepo.save(admin);
    return {
      id: admin.id,
      username: admin.username,
      name: admin.name,
      role: admin.role,
    };
  }

  async getProfile(id: number) {
    const admin = await this.adminRepo.findOne({ where: { id } });
    if (!admin) throw new NotFoundException('管理员不存在');
    return admin;
  }

  async updateProfile(id: number, dto: UpdateAdminDto) {
    await this.adminRepo.update(id, dto);
    return this.getProfile(id);
  }

  async changePassword(id: number, dto: ChangeAdminPasswordDto) {
    const admin = await this.adminRepo
      .createQueryBuilder('admin')
      .where('admin.id = :id', { id })
      .addSelect('admin.password')
      .getOne();

    if (!admin) throw new NotFoundException('管理员不存在');

    const isMatch = await bcrypt.compare(dto.oldPassword, admin.password);
    if (!isMatch) throw new BadRequestException('旧密码错误');

    await this.adminRepo.update(id, {
      password: await bcrypt.hash(dto.newPassword, 10),
    });
    return { message: '密码修改成功' };
  }

  async findAll(page = 1, pageSize = 10) {
    const [list, total] = await this.adminRepo.findAndCount({
      order: { createdAt: 'DESC' },
      skip: (page - 1) * pageSize,
      take: pageSize,
    });
    return {
      list,
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

  async freeze(id: number) {
    await this.adminRepo.update(id, { status: 'frozen' });
    return { message: '已冻结' };
  }

  async unfreeze(id: number) {
    await this.adminRepo.update(id, { status: 'active' });
    return { message: '已解冻' };
  }

  private async generateToken(admin: Admin) {
    const payload = {
      sub: admin.id,
      username: admin.username,
      role: 'admin',
      adminRole: admin.role,
      type: 'admin',
    };
    const accessToken = await this.jwtService.signAsync(payload);
    const refreshExpiresIn =
      this.configService.get<string>('jwt.refreshExpiresIn') || '7d';
    const refreshToken = await this.jwtService.signAsync(payload, {
      expiresIn: refreshExpiresIn as any,
    });

    return {
      accessToken,
      refreshToken,
      adminInfo: {
        id: admin.id,
        username: admin.username,
        name: admin.name,
        role: admin.role,
        phone: admin.phone,
      },
    };
  }
}
