import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';

export type RiskTier = 'low' | 'medium' | 'high';

export interface AttestationContext {
  platform: string;
  token: string;
  deviceId: string;
  riskTier: RiskTier;
  score: number;
  reason: string;
}

function normalizeHeader(value: unknown): string {
  if (typeof value === 'string') return value.trim();
  if (Array.isArray(value) && value[0]) return String(value[0]).trim();
  return '';
}

function scoreAttestation(input: {
  platform: string;
  token: string;
  deviceId: string;
  hasAuthUser: boolean;
}): AttestationContext {
  const { platform, token, deviceId, hasAuthUser } = input;
  let score = 0;
  const reasons: string[] = [];

  if (hasAuthUser) score += 40;
  else reasons.push('missing_auth_user');

  if (platform === 'ios' || platform === 'android') score += 20;
  else reasons.push('unknown_platform');

  if (token.length > 24) score += 20;
  else reasons.push('short_or_missing_token');

  if (deviceId.length >= 8) score += 20;
  else reasons.push('missing_device_id');

  let riskTier: RiskTier = 'low';
  if (score < 30) riskTier = 'high';
  else if (score < 60) riskTier = 'medium';

  return { platform, token, deviceId, riskTier, score, reason: reasons.join(',') || 'ok' };
}

/**
 * Risk-based device attestation — port of Express `requireAttestationRiskBased`.
 * Scores the request from attestation headers + auth presence and blocks
 * high-risk traffic with `403 ATTESTATION_REQUIRED`. The computed context is
 * attached to `req.attestation` for downstream use. Apply *after* AuthGuard.
 */
@Injectable()
export class AttestationGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const req = context.switchToHttp().getRequest();

    const platform = normalizeHeader(
      req.headers['x-app-attestation-platform'] || req.headers['x-attestation-platform'],
    ).toLowerCase();
    const token = normalizeHeader(
      req.headers['x-app-attestation-token'] || req.headers['x-attestation-token'],
    );
    const deviceId = normalizeHeader(req.headers['x-device-id'] || req.headers['device-id']);

    const ctx = scoreAttestation({
      platform,
      token,
      deviceId,
      hasAuthUser: !!(req.user?.id || req.user_id),
    });
    req.attestation = ctx;

    if (ctx.riskTier === 'high') {
      throw new ForbiddenException({
        status: false,
        statusCode: 403,
        message: 'Request blocked by risk policy. Device attestation is required.',
        code: 'ATTESTATION_REQUIRED',
        data: { riskTier: ctx.riskTier, reason: ctx.reason },
      });
    }

    return true;
  }
}
