import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { createHash } from 'crypto';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import { ApplicationError } from '@/shared/exceptions/domain.exception';

export interface TokenPayload {
  userId: string;
  accountSequence: string; // 格式: D + YYMMDD + 5位序号
  deviceId: string;
  type: 'access' | 'refresh';
}

@Injectable()
export class TokenService {
  constructor(
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    private readonly prisma: PrismaService,
  ) {}

  async generateTokenPair(payload: {
    userId: string;
    accountSequence: string; // 格式: D + YYMMDD + 5位序号
    deviceId: string;
  }): Promise<{ accessToken: string; refreshToken: string }> {
    const accessToken = this.jwtService.sign(
      { ...payload, type: 'access' },
      {
        expiresIn: this.configService.get<string>(
          'JWT_ACCESS_EXPIRES_IN',
          '2h',
        ),
      },
    );

    const refreshToken = this.jwtService.sign(
      { ...payload, type: 'refresh' },
      {
        expiresIn: this.configService.get<string>(
          'JWT_REFRESH_EXPIRES_IN',
          '30d',
        ),
      },
    );

    // Save refresh token hash
    const tokenHash = this.hashToken(refreshToken);

    // 先撤销该设备的旧 token，避免唯一约束冲突
    await this.prisma.deviceToken.updateMany({
      where: {
        userId: BigInt(payload.userId),
        deviceId: payload.deviceId,
        revokedAt: null,
      },
      data: { revokedAt: new Date() },
    });

    // 使用 upsert 避免并发请求导致的唯一约束冲突
    await this.prisma.deviceToken.upsert({
      where: { refreshTokenHash: tokenHash },
      update: {
        userId: BigInt(payload.userId),
        deviceId: payload.deviceId,
        expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        revokedAt: null,
      },
      create: {
        userId: BigInt(payload.userId),
        deviceId: payload.deviceId,
        refreshTokenHash: tokenHash,
        expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
      },
    });

    return { accessToken, refreshToken };
  }

  async verifyRefreshToken(token: string): Promise<{
    userId: string;
    accountSequence: string;
    deviceId: string;
  }> {
    try {
      const payload = this.jwtService.verify<TokenPayload>(token);
      if (payload.type !== 'refresh') {
        throw new ApplicationError('无效的RefreshToken');
      }

      const tokenHash = this.hashToken(token);
      const storedToken = await this.prisma.deviceToken.findUnique({
        where: { refreshTokenHash: tokenHash },
      });

      if (!storedToken || storedToken.revokedAt) {
        throw new ApplicationError('RefreshToken已失效');
      }

      return {
        userId: payload.userId,
        accountSequence: payload.accountSequence,
        deviceId: payload.deviceId,
      };
    } catch (error) {
      if (error instanceof ApplicationError) throw error;
      throw new ApplicationError('RefreshToken已过期或无效');
    }
  }

  async revokeDeviceTokens(userId: string, deviceId: string): Promise<void> {
    await this.prisma.deviceToken.updateMany({
      where: { userId: BigInt(userId), deviceId, revokedAt: null },
      data: { revokedAt: new Date() },
    });
  }

  private hashToken(token: string): string {
    return createHash('sha256').update(token).digest('hex');
  }
}
