import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
  Logger,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { IS_PUBLIC_KEY } from '../../common/guards/decorators/public.decorator';

/**
 * CLIENT AUTH GUARD
 * 
 * Validates JWT tokens for client portal endpoints
 * 
 * Key differences from standard JWT guard:
 * 1. Validates token is from a client (is_client=1)
 * 2. Ensures company_ids are present
 * 3. Logs client activity
 * 4. PORTAL-FIX: Checks company_users.status on EVERY request
 *    so disabled users are blocked INSTANTLY, even with a valid JWT
 * 
 * Usage:
 * @UseGuards(ClientAuthGuard)
 * @Get('protected-route')
 * async protectedRoute(@Req() req: Request) { ... }
 */
@Injectable()
export class ClientAuthGuard extends AuthGuard('jwt') {
  private readonly logger = new Logger('ClientAuthGuard');

  constructor(
    private reflector: Reflector,
    @InjectDataSource('scheme_dbs')
    private readonly dataSource: DataSource,
  ) {
    super();
  }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const path = request.path;

    this.logger.debug(`→ Request path: ${path}`);

    // Allow @Public() decorated routes without token
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (isPublic) {
      this.logger.debug(`✓ Public route, no auth required`);
      return true;
    }

    // Allow login/invite routes without token (public endpoints)
    if (
      path.includes('/client-portal/login') ||
      path.includes('/client-portal/invite') ||
      path.includes('/client-portal/verify-otp') ||
      path.includes('/client-portal/resend-otp')
    ) {
      this.logger.debug(`✓ Auth endpoint, no token required`);
      return true;
    }

    // Validate JWT token (calls handleRequest below)
    const result = await (super.canActivate(context) as Promise<boolean>);
    if (!result) return false;

    // PORTAL-FIX: After JWT is valid, check company_users.status in DB.
    //    This blocks users who were disabled AFTER their JWT was issued.
    //    Without this, a disabled user keeps access until JWT expires (30 days).
    const user = request.user;
    if (user && (user.sub || user.id)) {
      try {
        const activeCount = await this.dataSource.query(
          `SELECT COUNT(*) as cnt FROM company_users 
           WHERE user_id = ? AND role = 'CLIENT' AND status = 'ACTIVE'`,
          [user.sub || user.id],
        );
        const count = Number(activeCount?.[0]?.cnt ?? 0);
        if (count === 0) {
          this.logger.warn(
            `❌ User ${user.sub || user.id} has valid JWT but ALL company_users are DISABLED — blocking`,
          );
          throw new UnauthorizedException(
            'Your portal access has been disabled. Please contact your coordinator.',
          );
        }
      } catch (err: any) {
        // If it's our own UnauthorizedException, re-throw it
        if (err instanceof UnauthorizedException) throw err;
        // DB errors should not block the user — log and allow
        this.logger.warn(`⚠️ Status check failed: ${err?.message} — allowing request`);
      }
    }

    return true;
  }

  handleRequest(err: any, user: any, info: any) {
    this.logger.debug(`JWT Guard validation`);

    if (err || !user) {
      this.logger.warn(`❌ JWT validation failed: ${err?.message || 'no user'}`);
      throw new UnauthorizedException('Invalid or missing token');
    }

    // Validate user is a client
    if (user.is_client !== 1) {
      this.logger.warn(`❌ User is not a client: user_id=${user.id}`);
      throw new UnauthorizedException('Access restricted to clients only');
    }

    // Validate company_ids are present
    if (!user.company_ids || user.company_ids.length === 0) {
      this.logger.warn(`❌ No companies assigned: user_id=${user.id}`);
      throw new UnauthorizedException('No company assigned to this client');
    }

    this.logger.log(
      `✓ Client authenticated: user_id=${user.id}, companies=[${user.company_ids.join(',')}]`,
    );

    return user;
  }
}