import { CanActivate, ExecutionContext, Injectable, Inject, Logger } from '@nestjs/common';
import { Request } from 'express';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { ITokenPayload } from '../interfaces/IToken-payload';

export interface UserOptionalAuthRequest extends Request {
  userId?: string;
  restId?: string;
  slug?: string;
}

@Injectable()
export class OptionalAuthGuard implements CanActivate {
  private readonly logger = new Logger(OptionalAuthGuard.name);

  constructor(
    @Inject(JwtService)
    private readonly jwtService: JwtService,
    @Inject(ConfigService)
    private readonly configService: ConfigService,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest<UserOptionalAuthRequest>();
    const token = this.extractTokenFromHeader(request);
    const slug = this.extractSlugFromHeader(request);

    // If no token is provided, allow the request without authentication
    if (!slug) {
      console.log('No slug provided');
      this.logger.warn('No slug provided');
      return false;
    }
    if (!token) {
      request['slug'] = slug;
      return true;
    }

    // Try to verify the token if it exists
    try {
      const secret = this.configService.getOrThrow<string>('JWT_SECRET');
      const payload = await this.jwtService.verifyAsync<ITokenPayload>(token, {
        secret,
      });

      // Validate payload structure
      if (!payload.userId || !payload.restId) {
        this.logger.warn('Invalid token payload structure', payload);
        // Allow request but don't set user info
        request['slug'] = slug;
        return true;
      }

      // Validate slug if provided
      if (slug && payload.slug !== slug) {
        this.logger.warn('Token slug does not match header slug', {
          tokenSlug: payload.slug,
          headerSlug: slug,
        });
        // Allow request but don't set user info
        request['slug'] = slug;
        return true;
      }

      // Token is valid, set user info
      request['userId'] = payload.userId;
      request['restId'] = payload.restId;
      request['slug'] = slug;
      return true;
    } catch (err) {
      // Token is invalid or expired, but allow the request anyway
      this.logger.debug('Token verification failed in OptionalAuthGuard', {
        error: err instanceof Error ? err.message : 'Unknown error',
      });
      // Set restId from slug

      request['slug'] = slug;

      return true;
    }
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }

  private extractSlugFromHeader(request: Request): string | undefined {
    // Fastify normalizes headers to lowercase, so check both cases
    const slug = (request.headers['x-slug'] || request.headers['X-Slug']) as string;
    return slug;
  }
}
