/**
 * WordPress Authentication Handler
 * Manages different authentication methods for WordPress REST API
 */

import { randomBytes } from "crypto";
import { LoggerFactory } from "@/utils/logger.js";

const log = LoggerFactory.client("AUTH");
import { URL } from "url";
import type {
  IAuthProvider,
  IWordPressClient,
  AuthMethod,
  AuthConfig,
  AuthenticationError as _AuthenticationError,
} from "@/types/client.js";
import type { WordPressUser } from "@/types/wordpress.js";

export class WordPressAuth {
  private client: IWordPressClient;
  private authType: AuthMethod;

  constructor(client: IWordPressClient) {
    this.client = client;
    this.authType = client.config.auth.method;
  }

  /**
   * Handle authentication based on type
   */
  async authenticate(): Promise<boolean> {
    try {
      switch (this.authType) {
        case "app-password":
          return await this.handleAppPasswordAuth();
        case "jwt":
          return await this.handleJWTAuth();
        case "basic":
          return await this.handleBasicAuth();
        case "api-key":
          return await this.handleAPIKeyAuth();
        case "cookie":
          return await this.handleCookieAuth();
        default:
          throw new Error(`Unsupported authentication type: ${this.authType}`);
      }
    } catch (_error) {
      log.error("Authentication failed", { error: String(_error) });
      throw _error;
    }
  }

  /**
   * Handle Application Password authentication
   */
  private async handleAppPasswordAuth(): Promise<boolean> {
    const { username, appPassword } = this.client.config.auth;

    if (!username || !appPassword) {
      throw new Error(
        "Application Password authentication requires WORDPRESS_USERNAME and WORDPRESS_APP_PASSWORD. " +
          "Visit your WordPress admin → Users → Profile → Application Passwords to create one.",
      );
    }

    // Test the credentials by attempting to get current user
    try {
      const user = await this.client.getCurrentUser();
      log.debug(`Application Password authentication successful for user: ${user.name} (${user.username})`);
      return true;
    } catch (_error) {
      const message =
        "Application Password authentication failed. Please check your credentials and ensure the application password is valid.";
      log.error(message, { error: String(_error) });
      throw new Error(message);
    }
  }

  /**
   * Handle Basic authentication (username + password)
   */
  private async handleBasicAuth(): Promise<boolean> {
    const { username, password } = this.client.config.auth;

    if (!username || !password) {
      throw new Error("Basic authentication requires WORDPRESS_USERNAME and WORDPRESS_PASSWORD");
    }

    try {
      const user = await this.client.getCurrentUser();
      log.debug(`Basic authentication successful for user: ${user.name} (${user.username})`);
      return true;
    } catch (_error) {
      const message = "Basic authentication failed. Please check your username and password.";
      log.error(message, { error: String(_error) });
      throw new Error(message);
    }
  }

  /**
   * Handle JWT authentication
   */
  private async handleJWTAuth(): Promise<boolean> {
    const { username, password, secret } = this.client.config.auth;

    if (!username || !password || !secret) {
      throw new Error(
        "JWT authentication requires WORDPRESS_USERNAME, WORDPRESS_PASSWORD, and WORDPRESS_JWT_SECRET. " +
          "Install and configure the JWT Authentication plugin first.",
      );
    }

    try {
      // The JWT token should be obtained during client authentication
      const user = await this.client.getCurrentUser();
      log.debug(`JWT authentication successful for user: ${user.name} (${user.username})`);
      return true;
    } catch (_error) {
      const message =
        "JWT authentication failed. Please check your credentials and ensure the JWT plugin is installed and configured.";
      log.error(message, { error: String(_error) });
      throw new Error(message);
    }
  }

  /**
   * Handle API Key authentication
   */
  private async handleAPIKeyAuth(): Promise<boolean> {
    const { apiKey } = this.client.config.auth;

    if (!apiKey) {
      throw new Error("API Key authentication requires WORDPRESS_API_KEY");
    }

    try {
      // Test API key by making a simple request
      await this.client.getSiteInfo();
      log.debug("API Key authentication successful");
      return true;
    } catch (_error) {
      const message = "API Key authentication failed. Please check your API key.";
      log.error(message, { error: String(_error) });
      throw new Error(message);
    }
  }

  /**
   * Handle Cookie authentication
   */
  private async handleCookieAuth(): Promise<boolean> {
    const { nonce } = this.client.config.auth;

    if (!nonce) {
      log.warn("Cookie authentication: No nonce provided, authentication may fail for write operations");
    }

    try {
      // Test with a simple read operation
      await this.client.getSiteInfo();
      log.debug("Cookie authentication configured (note: write operations may require valid nonce)");
      return true;
    } catch (_error) {
      const message = "Cookie authentication failed. Please ensure you are properly logged into WordPress.";
      log.error(message, { error: String(_error) });
      throw new Error(message);
    }
  }

  /**
   * Refresh authentication (for JWT and OAuth)
   */
  async refreshAuth(): Promise<boolean> {
    switch (this.authType) {
      case "jwt":
        return await this.refreshJWTToken();
      default:
        log.debug(`Authentication refresh not supported for ${this.authType}`);
        return true;
    }
  }

  /**
   * Refresh JWT token
   */
  private async refreshJWTToken(): Promise<boolean> {
    try {
      // Re-authenticate to get a new token
      return await this.handleJWTAuth();
    } catch (_error) {
      log.error("Failed to refresh JWT token", { error: String(_error) });
      return false;
    }
  }

  /**
   * Validate current authentication
   */
  async validateAuth(): Promise<boolean> {
    try {
      await this.client.getCurrentUser();
      return true;
    } catch (_error) {
      log.error("Authentication validation failed", { error: String(_error) });
      return false;
    }
  }

  /**
   * Get authentication status information
   */
  async getAuthStatus(): Promise<{
    authenticated: boolean;
    method: AuthMethod;
    user?: WordPressUser;
    error?: string;
  }> {
    try {
      const user = await this.client.getCurrentUser();
      return {
        authenticated: true,
        method: this.authType,
        user,
      };
    } catch (_error) {
      return {
        authenticated: false,
        method: this.authType,
        error: (_error as Error).message,
      };
    }
  }

  /**
   * Switch authentication method
   */
  async switchAuthMethod(newConfig: AuthConfig): Promise<boolean> {
    // Update client configuration
    (this.client.config as { auth: AuthConfig }).auth = newConfig;
    this.authType = newConfig.method;

    // Re-authenticate with new method
    return await this.authenticate();
  }

  /**
   * Start OAuth 2.0 flow (for future implementation)
   */
  async startOAuthFlow(): Promise<{ authUrl: string; state: string }> {
    const { clientId } = this.client.config.auth;

    if (!clientId) {
      throw new Error("OAuth requires client ID");
    }

    const state = this.generateRandomState();
    const redirectUri = "http://localhost:8080/oauth/callback";

    const authUrl = new URL("/oauth/authorize", this.client.config.baseUrl);
    authUrl.searchParams.append("client_id", clientId);
    authUrl.searchParams.append("redirect_uri", redirectUri);
    authUrl.searchParams.append("response_type", "code");
    authUrl.searchParams.append("state", state);
    authUrl.searchParams.append("scope", "read write");

    return {
      authUrl: authUrl.toString(),
      state,
    };
  }

  /**
   * Complete OAuth 2.0 flow (for future implementation)
   */
  async completeOAuthFlow(code: string, state: string): Promise<boolean> {
    // This would implement the OAuth token exchange
    // For now, this is a placeholder
    throw new Error("OAuth flow not yet implemented");
  }

  /**
   * Generate random state for OAuth
   */
  private generateRandomState(length = 32): string {
    return randomBytes(Math.ceil((length * 3) / 4))
      .toString("base64url")
      .slice(0, length);
  }

  /**
   * Get authentication headers for the current method
   */
  getAuthHeaders(): Record<string, string> {
    const headers: Record<string, string> = {};
    const auth = this.client.config.auth;

    switch (this.authType) {
      case "app-password":
        if (auth.username && auth.appPassword) {
          const credentials = Buffer.from(`${auth.username}:${auth.appPassword}`).toString("base64");
          headers["Authorization"] = `Basic ${credentials}`;
        }
        break;
      case "basic":
        if (auth.username && auth.password) {
          const credentials = Buffer.from(`${auth.username}:${auth.password}`).toString("base64");
          headers["Authorization"] = `Basic ${credentials}`;
        }
        break;

      case "jwt":
        if (auth.token) {
          headers["Authorization"] = `Bearer ${auth.token}`;
        }
        break;

      case "api-key":
        if (auth.apiKey) {
          headers["X-API-Key"] = auth.apiKey;
        }
        break;

      case "cookie":
        if (auth.nonce) {
          headers["X-WP-Nonce"] = auth.nonce;
        }
        break;
    }

    return headers;
  }

  /**
   * Check if authentication method requires additional setup
   */
  requiresSetup(): boolean {
    switch (this.authType) {
      case "jwt":
        return !this.client.config.auth.secret;
      case "api-key":
        return !this.client.config.auth.apiKey;
      case "app-password":
        return !this.client.config.auth.username || !this.client.config.auth.appPassword;
      case "basic":
        return !this.client.config.auth.username || !this.client.config.auth.password;
      case "cookie":
        return false; // Cookie auth can work without additional setup
      default:
        return true;
    }
  }

  /**
   * Get setup instructions for the current authentication method
   */
  getSetupInstructions(): string {
    switch (this.authType) {
      case "app-password":
        return `
To set up Application Password authentication:
1. Log into your WordPress admin dashboard
2. Go to Users → Profile (or Users → All Users → Edit your user)
3. Scroll down to "Application Passwords" section
4. Enter a name for this application (e.g., "MCP WordPress Server")
5. Click "Add New Application Password"
6. Copy the generated password and set it as WORDPRESS_APP_PASSWORD
7. Set WORDPRESS_USERNAME to your WordPress username
`;

      case "jwt":
        return `
To set up JWT authentication:
1. Install the "JWT Authentication for WP REST API" plugin
2. Add JWT_AUTH_SECRET_KEY to your wp-config.php file
3. Configure the plugin settings
4. Set WORDPRESS_JWT_SECRET environment variable
5. Set WORDPRESS_USERNAME and WORDPRESS_PASSWORD
`;

      case "api-key":
        return `
To set up API Key authentication:
1. Install an API Key plugin (varies by plugin)
2. Generate an API key in the plugin settings
3. Set WORDPRESS_API_KEY environment variable
`;

      case "basic":
        return `
To set up Basic authentication:
1. Set WORDPRESS_USERNAME to your WordPress username
2. Set WORDPRESS_PASSWORD to your WordPress password
Note: This method is less secure than Application Passwords
`;

      case "cookie":
        return `
Cookie authentication is automatically configured when you're logged into WordPress.
For write operations, you may need to set WORDPRESS_COOKIE_NONCE.
`;

      default:
        return "No setup instructions available for this authentication method.";
    }
  }
}

/**
 * Authentication Provider implementations
 */

export class AppPasswordAuthProvider implements IAuthProvider {
  readonly method: AuthMethod = "app-password";

  async authenticate(client: IWordPressClient): Promise<boolean> {
    const auth = new WordPressAuth(client);
    return auth.authenticate();
  }

  addAuthHeaders(headers: Record<string, string>): void {
    // Implementation handled by WordPressAuth
  }
}

export class JWTAuthProvider implements IAuthProvider {
  readonly method: AuthMethod = "jwt";

  async authenticate(client: IWordPressClient): Promise<boolean> {
    const auth = new WordPressAuth(client);
    return auth.authenticate();
  }

  addAuthHeaders(headers: Record<string, string>): void {
    // Implementation handled by WordPressAuth
  }

  async refreshAuth(): Promise<boolean> {
    // JWT token refresh logic
    return true;
  }
}

export class BasicAuthProvider implements IAuthProvider {
  readonly method: AuthMethod = "basic";

  async authenticate(client: IWordPressClient): Promise<boolean> {
    const auth = new WordPressAuth(client);
    return auth.authenticate();
  }

  addAuthHeaders(headers: Record<string, string>): void {
    // Implementation handled by WordPressAuth
  }
}

export class APIKeyAuthProvider implements IAuthProvider {
  readonly method: AuthMethod = "api-key";

  async authenticate(client: IWordPressClient): Promise<boolean> {
    const auth = new WordPressAuth(client);
    return auth.authenticate();
  }

  addAuthHeaders(headers: Record<string, string>): void {
    // Implementation handled by WordPressAuth
  }
}

export class CookieAuthProvider implements IAuthProvider {
  readonly method: AuthMethod = "cookie";

  async authenticate(client: IWordPressClient): Promise<boolean> {
    const auth = new WordPressAuth(client);
    return auth.authenticate();
  }

  addAuthHeaders(headers: Record<string, string>): void {
    // Implementation handled by WordPressAuth
  }
}

/**
 * Factory function to create appropriate auth provider
 */
export function createAuthProvider(method: AuthMethod): IAuthProvider {
  switch (method) {
    case "app-password":
      return new AppPasswordAuthProvider();
    case "jwt":
      return new JWTAuthProvider();
    case "basic":
      return new BasicAuthProvider();
    case "api-key":
      return new APIKeyAuthProvider();
    case "cookie":
      return new CookieAuthProvider();
    default:
      throw new Error(`Unsupported authentication method: ${method}`);
  }
}
