import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from "@nestjs/common";
import { AuthService } from "../auth.service";
import type { AuthUser } from "@mosaic/shared";

/**
 * Request type with authentication context
 */
interface AuthRequest {
  user?: AuthUser;
  session?: Record<string, unknown>;
  headers: Record<string, string | string[] | undefined>;
  cookies?: Record<string, string>;
}

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly authService: AuthService) {}

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

    // Try to get token from either cookie (preferred) or Authorization header
    const token = this.extractToken(request);

    if (!token) {
      throw new UnauthorizedException("No authentication token provided");
    }

    try {
      const sessionData = await this.authService.verifySession(token);

      if (!sessionData) {
        throw new UnauthorizedException("Invalid or expired session");
      }

      // Attach user and session to request
      const user = sessionData.user;
      // Validate user has required fields
      if (typeof user !== "object" || !("id" in user) || !("email" in user) || !("name" in user)) {
        throw new UnauthorizedException("Invalid user data in session");
      }
      request.user = user as unknown as AuthUser;
      request.session = sessionData.session;

      return true;
    } catch (error) {
      // Re-throw if it's already an UnauthorizedException
      if (error instanceof UnauthorizedException) {
        throw error;
      }
      throw new UnauthorizedException("Authentication failed");
    }
  }

  /**
   * Extract token from cookie (preferred) or Authorization header
   */
  private extractToken(request: AuthRequest): string | undefined {
    // Try cookie first (BetterAuth default)
    const cookieToken = this.extractTokenFromCookie(request);
    if (cookieToken) {
      return cookieToken;
    }

    // Fallback to Authorization header for API clients
    return this.extractTokenFromHeader(request);
  }

  /**
   * Extract token from cookie (BetterAuth stores session token in better-auth.session_token cookie)
   */
  private extractTokenFromCookie(request: AuthRequest): string | undefined {
    if (!request.cookies) {
      return undefined;
    }

    // BetterAuth uses 'better-auth.session_token' as the cookie name by default
    return request.cookies["better-auth.session_token"];
  }

  /**
   * Extract token from Authorization header (Bearer token)
   */
  private extractTokenFromHeader(request: AuthRequest): string | undefined {
    const authHeader = request.headers.authorization;
    if (typeof authHeader !== "string") {
      return undefined;
    }

    const parts = authHeader.split(" ");
    const [type, token] = parts;

    return type === "Bearer" ? token : undefined;
  }
}
