import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import jwt from 'jsonwebtoken';
import type { FamilyActorContext } from '../../common/family-actor-context.js';
import { MESSAGES } from '../../common/messages.js';
import { PrismaService } from '../../prisma.service.js';

/**
 * C 端小程序登录守卫。
 */
@Injectable()
export class FamilyAccessTokenGuard implements CanActivate {
  constructor(private readonly prisma: PrismaService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest<{
      headers: Record<string, string | string[] | undefined>;
      familyActor?: FamilyActorContext;
    }>();
    const authorization = request.headers.authorization;
    const headerValue = Array.isArray(authorization)
      ? authorization[0]
      : authorization;

    if (!headerValue || !headerValue.startsWith('Bearer ')) {
      throw new UnauthorizedException(MESSAGES.AUTH.MISSING_BEARER);
    }

    request.familyActor = await this.verifyAndExtractActor(
      headerValue.slice('Bearer '.length).trim(),
    );
    return true;
  }

  /**
   * 校验 C 端 token 并回库确认账号仍存在。
   */
  private async verifyAndExtractActor(
    token: string,
  ): Promise<FamilyActorContext> {
    const secret = process.env.AUTH_TOKEN_SECRET;
    if (!secret) {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_SECRET_MISSING);
    }

    let payload: string | jwt.JwtPayload;
    try {
      payload = jwt.verify(token, secret, {
        algorithms: ['HS256'],
        issuer: 'tyt-api-nest-family',
      });
    } catch {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_INVALID);
    }

    if (
      typeof payload !== 'object' ||
      payload.type !== 'FAMILY_MINIAPP' ||
      typeof payload.id !== 'number' ||
      !Number.isInteger(payload.id)
    ) {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_PAYLOAD_INVALID);
    }

    const account = await this.prisma.familyMiniAppAccount.findUnique({
      where: { id: payload.id },
      select: {
        id: true,
        phone: true,
        openId: true,
        serviceUid: true,
      },
    });
    if (!account) {
      throw new UnauthorizedException(MESSAGES.AUTH.FAMILY_ACCOUNT_NOT_FOUND);
    }

    return account;
  }
}
