import { Injectable } from '@nestjs/common';
import { HttpStatus } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { AppleAuthService } from './apple-auth.service';
import { TokenService } from './token.service';
import { CapiException } from '../../common/errors/capi.exception';
import { CAPIErrorCode } from '../../common/errors/capi-error-codes';
import type { AppleLoginDto, DevLoginDto } from './dto';

@Injectable()
export class AuthService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly appleAuthService: AppleAuthService,
    private readonly tokenService: TokenService,
  ) {}

  async devLogin(dto: DevLoginDto) {
    if (process.env.NODE_ENV === 'production') {
      throw new CapiException(CAPIErrorCode.AUTH_DEV_LOGIN_FORBIDDEN, 'dev-login is disabled in production', HttpStatus.FORBIDDEN);
    }

    const devSecret = process.env.DEV_SECRET;
    if (!devSecret || dto.devSecret !== devSecret) {
      throw new CapiException(CAPIErrorCode.AUTH_UNAUTHORIZED, 'devSecret 无效', HttpStatus.UNAUTHORIZED);
    }

    const providerUserId = dto.email;

    let account = await this.prisma.authAccount.findUnique({
      where: {
        provider_providerUserId: {
          provider: 'DEV',
          providerUserId,
        },
      },
      include: { user: true },
    });

    if (!account) {
      account = await this.prisma.authAccount.create({
        data: {
          provider: 'DEV',
          providerUserId,
          email: dto.email,
          user: {
            create: {
              email: dto.email,
              nickname: dto.nickname || '测试用户',
              status: 'active',
            },
          },
        },
        include: { user: true },
      });
    }

    return this.buildLoginResponse(account.user);
  }

  async appleLogin(dto: AppleLoginDto) {
    const { appleUserId, email: appleEmail } =
      await this.appleAuthService.verifyIdentityToken(
        dto.identityToken,
        dto.nonce,
      );

    let account = await this.prisma.authAccount.findUnique({
      where: {
        provider_providerUserId: {
          provider: 'APPLE',
          providerUserId: appleUserId,
        },
      },
      include: { user: true },
    });

    if (!account) {
      const displayName =
        dto.fullName?.givenName
          ? `${dto.fullName.familyName || ''}${dto.fullName.givenName}`
          : undefined;

      // M-MEMBER-01-02: Email-based deduplication — find existing user by email first
      const existingEmail = appleEmail || dto.email || null;
      let existingUser: { id: string; appAccountToken: string | null } | null = null;
      if (existingEmail) {
        existingUser = await this.prisma.user.findFirst({
          where: { email: existingEmail },
          select: { id: true, appAccountToken: true },
        });
      }

      const userCreateOrConnect = existingUser
        ? { connect: { id: existingUser.id } }
        : {
            create: {
              email: existingEmail,
              nickname: displayName || undefined,
              status: 'active',
            },
          };

      account = await this.prisma.authAccount.create({
        data: {
          provider: 'APPLE',
          providerUserId: appleUserId,
          email: existingEmail,
          user: userCreateOrConnect,
        },
        include: { user: true },
      });
    } else {
      // 已有账户：如果首次登录时 nickname 未成功写入（网络中断等），
      // 后续 Apple 返回 fullName 时补写
      if (!account.user.nickname && dto.fullName?.givenName) {
        const displayName = `${dto.fullName.familyName || ''}${dto.fullName.givenName}`;
        await this.prisma.user.update({
          where: { id: account.userId },
          data: { nickname: displayName },
        });
      }
      // 补写首次可能缺失的 email
      if (!account.user.email && appleEmail) {
        await this.prisma.user.update({
          where: { id: account.userId },
          data: { email: appleEmail },
        });
      }
      if (!account.email && appleEmail) {
        await this.prisma.authAccount.update({
          where: { id: account.id },
          data: { email: appleEmail },
        });
      }
    }

    return this.buildLoginResponse(account.user);
  }

  async refresh(refreshToken: string) {
    const hash = this.tokenService.hashToken(refreshToken);
    const stored = await this.prisma.refreshToken.findFirst({
      where: { tokenHash: hash, revokedAt: null },
      include: { user: true },
    });

    if (!stored) {
      throw new CapiException(CAPIErrorCode.AUTH_REFRESH_TOKEN_REVOKED, '刷新令牌已失效', HttpStatus.UNAUTHORIZED);
    }

    if (stored.expiresAt < new Date()) {
      throw new CapiException(CAPIErrorCode.AUTH_REFRESH_TOKEN_EXPIRED, '刷新令牌已过期，请重新登录', HttpStatus.UNAUTHORIZED);
    }

    // Check user status before issuing new tokens
    if (stored.user.deletedAt) {
      await this.revokeAllUserTokens(stored.userId);
      throw new CapiException(CAPIErrorCode.AUTH_USER_DELETED, '账号已注销', HttpStatus.UNAUTHORIZED);
    }

    if (stored.user.status !== 'active') {
      throw new CapiException(CAPIErrorCode.AUTH_USER_DISABLED, '账号已被禁用', HttpStatus.UNAUTHORIZED);
    }

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

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

    await this.prisma.refreshToken.create({
      data: {
        userId: stored.userId,
        tokenHash: newHash,
        expiresAt: new Date(Date.now() + 7 * 86400000),
      },
    });

    const accessToken = await this.tokenService.generateAccessToken(
      stored.user,
    );

    return {
      accessToken,
      refreshToken: newRefreshToken,
      user: this.serializeUser(stored.user),
    };
  }

  private async revokeAllUserTokens(userId: string) {
    await this.prisma.refreshToken.updateMany({
      where: { userId, revokedAt: null },
      data: { revokedAt: new Date() },
    });
  }

  async logout(userId: string, refreshToken: string) {
    const hash = this.tokenService.hashToken(refreshToken);
    const stored = await this.prisma.refreshToken.findFirst({
      where: {
        tokenHash: hash,
        userId,
        revokedAt: null,
      },
    });

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

  private async buildLoginResponse(user: {
    id: string;
    email: string | null;
    nickname: string | null;
    avatarUrl: string | null;
    role: string;
    status: string;
    onboardingCompleted: boolean;
  }) {
    const accessToken = await this.tokenService.generateAccessToken(user);

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

    await this.prisma.refreshToken.create({
      data: {
        userId: user.id,
        tokenHash: hash,
        expiresAt: new Date(Date.now() + 7 * 86400000),
      },
    });

    return {
      accessToken,
      refreshToken,
      user: this.serializeUser(user),
    };
  }

  private serializeUser(user: {
    id: string;
    email: string | null;
    nickname: string | null;
    avatarUrl: string | null;
    role: string;
    status: string;
    onboardingCompleted: boolean;
  }) {
    return {
      id: user.id,
      email: user.email,
      nickname: user.nickname,
      avatarUrl: user.avatarUrl,
      role: user.role,
      status: user.status,
      onboardingCompleted: user.onboardingCompleted,
    };
  }
}
