import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { SalesforceRequestQueueService } from "@bff/infra/queue/services/salesforce-request-queue.service.js";
import jsforce from "jsforce";
import { SignJWT } from "jose";
import { createPrivateKey } from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";

// Constants for duplicate string literals
const REDACTED = "[REDACTED]";
const INVALID_PRIVATE_KEY_MSG = "Invalid Salesforce private key";
const SF_CONFIG_MISSING_MSG = "Salesforce configuration is missing";

type NormalizedKey = { kind: "pem"; value: string } | { kind: "b64"; value: string };

interface SalesforceAuthConfig {
  username: string;
  clientId: string;
  audience: string;
  privateKeyEnv: string | undefined;
  privateKeyPath: string | undefined;
}

interface TokenRequestOptions {
  config: SalesforceAuthConfig;
  assertion: string;
  authTimeout: number;
  startTime: number;
  isProd: boolean;
}

function normalizePrivateKeyInput(raw: string): NormalizedKey {
  // Handle common "pasted" forms:
  // - Windows newlines (\r\n)
  // - literal "\n" sequences (when passed through env/template systems)
  // - PEM with missing line breaks
  // Avoid regex control characters (eslint no-control-regex); remove null bytes safely.
  const trimmed = raw.trim().replaceAll("\u0000", "");

  // Convert literal backslash-n into real newlines (only if present)
  const withNewlines = trimmed.includes("\\n") ? trimmed.replaceAll("\\n", "\n") : trimmed;
  const normalizedNewlines = withNewlines.replace(/\r\n/g, "\n").replace(/\r/g, "\n");

  // If it looks like PEM, ensure headers/footers are on their own lines.
  if (normalizedNewlines.includes("-----BEGIN")) {
    // IMPORTANT: Keep normalization line-based. Avoid patterns that can span across the
    // base64 body (which often contains no '-' characters), otherwise we can corrupt
    // otherwise-valid keys.
    let pem = normalizedNewlines.trim();

    // Ensure the BEGIN line ends with a newline (only if it's missing).
    pem = pem.replace(/(-----BEGIN [^-\n]+-----)(?!\n)/g, "$1\n");

    // Ensure the END line starts on its own line (only if it's missing).
    pem = pem.replace(/([A-Za-z0-9+/=])\s*(-----END [^-\n]+-----)/g, "$1\n$2");

    return { kind: "pem", value: pem.trim() + "\n" };
  }

  // Otherwise, treat it as base64 DER if it is base64-ish.
  // (This supports secrets systems that store the key as base64 without PEM headers.)
  const b64 = normalizedNewlines.replace(/\s+/g, "");
  const looksBase64 = /^[A-Za-z0-9+/=]+$/.test(b64) && b64.length >= 256;
  if (looksBase64) {
    // Some systems store the PEM text itself as base64. If so, decode and retry as PEM.
    try {
      const decoded = Buffer.from(b64, "base64").toString("utf8").trim();
      if (decoded.includes("-----BEGIN")) {
        return normalizePrivateKeyInput(decoded);
      }
    } catch {
      // ignore and fall back to DER handling
    }
    return { kind: "b64", value: b64 };
  }

  return { kind: "pem", value: normalizedNewlines.trim() + "\n" };
}

export interface SalesforceSObjectApi {
  create: (data: Record<string, unknown>) => Promise<{ id?: string | undefined }>;
  update?: ((data: Record<string, unknown> & { Id: string }) => Promise<unknown>) | undefined;
}

@Injectable()
export class SalesforceConnection {
  private connection: jsforce.Connection;
  private tokenExpiresAt: number | null = null;
  private tokenIssuedAt: number | null = null;
  private connectPromise: Promise<void> | null = null;
  private organizationId: string | null = null;

  constructor(
    private configService: ConfigService,
    private readonly requestQueue: SalesforceRequestQueueService,
    @Inject(Logger) private readonly logger: Logger
  ) {
    this.connection = new jsforce.Connection({
      loginUrl: this.configService.get<string>("SF_LOGIN_URL") || "https://test.salesforce.com",
    });
  }

  /**
   * Expose jsforce connection for advanced features
   * Consumers must ensure connect() has been called and isConnected() is true.
   */
  getConnection(): jsforce.Connection {
    return this.connection;
  }

  /** Get the current OAuth access token (after connect()). */
  getAccessToken(): string | undefined {
    return this.connection?.accessToken as string | undefined;
  }

  /** Get the current instance URL (after connect()). */
  getInstanceUrl(): string | undefined {
    return this.connection?.instanceUrl as string | undefined;
  }

  async connect(force = false): Promise<void> {
    if (this.connectPromise && !force) {
      return this.connectPromise;
    }

    this.connectPromise = this.performConnect();
    try {
      await this.connectPromise;
    } finally {
      this.connectPromise = null;
    }
  }

  private async performConnect(): Promise<void> {
    const isProd = this.isProduction();
    const authTimeout = this.configService.get<number>("SF_AUTH_TIMEOUT_MS", 30000);
    const startTime = Date.now();

    this.logger.debug("Starting Salesforce authentication", {
      authTimeout,
      nodeEnv: this.configService.get<string>("NODE_ENV") || "development",
    });

    try {
      const config = this.getAuthConfig(isProd);
      const rawPrivateKey = await this.loadPrivateKey(config, isProd);
      const cryptoKey = this.createCryptoKey(rawPrivateKey, isProd);
      const assertion = await this.createJwtAssertion(config, cryptoKey);
      const tokenResponse = await this.requestAccessToken({
        config,
        assertion,
        authTimeout,
        startTime,
        isProd,
      });
      this.processTokenResponse(tokenResponse, startTime, isProd);
    } catch (error) {
      this.handleAuthError(error, startTime, isProd);
      throw error;
    }
  }

  private isProduction(): boolean {
    const nodeEnv =
      this.configService.get<string>("NODE_ENV") || process.env["NODE_ENV"] || "development";
    return nodeEnv === "production";
  }

  private getAuthConfig(isProd: boolean): SalesforceAuthConfig {
    const username = this.configService.get<string>("SF_USERNAME");
    const clientId = this.configService.get<string>("SF_CLIENT_ID");
    const privateKeyPath = this.configService.get<string>("SF_PRIVATE_KEY_PATH");
    const privateKeyEnv = this.configService.get<string>("SF_PRIVATE_KEY_BASE64");
    const audience =
      this.configService.get<string>("SF_LOGIN_URL") || "https://test.salesforce.com";

    if (!username || !clientId || (!privateKeyEnv && !privateKeyPath)) {
      const devMessage =
        "Missing required Salesforce configuration. Please check SF_USERNAME, SF_CLIENT_ID, and either SF_PRIVATE_KEY_BASE64 or SF_PRIVATE_KEY_PATH environment variables.";
      throw new Error(isProd ? SF_CONFIG_MISSING_MSG : devMessage);
    }

    return { username, clientId, audience, privateKeyEnv, privateKeyPath };
  }

  private async loadPrivateKey(config: SalesforceAuthConfig, isProd: boolean): Promise<string> {
    if (config.privateKeyEnv) {
      return config.privateKeyEnv;
    }
    return this.loadPrivateKeyFromFile(config.privateKeyPath, isProd);
  }

  private async loadPrivateKeyFromFile(
    keyPath: string | undefined,
    isProd: boolean
  ): Promise<string> {
    if (!keyPath) {
      const devMessage =
        "Missing required Salesforce configuration. Please check SF_PRIVATE_KEY_PATH environment variable.";
      throw new Error(isProd ? SF_CONFIG_MISSING_MSG : devMessage);
    }

    const resolvedKeyPath = this.resolveKeyPath(keyPath);
    this.validateKeyPathSecurity(resolvedKeyPath, isProd);
    await this.ensureKeyFileExists(resolvedKeyPath, isProd);
    return fs.readFile(resolvedKeyPath, "utf8");
  }

  private getProjectRoot(): string {
    const cwd = process.cwd();
    const norm = path.normalize(cwd);
    const bffSuffix = path.normalize(path.join("apps", "bff"));
    if (norm.endsWith(bffSuffix)) {
      return path.resolve(cwd, "../..");
    }
    return cwd;
  }

  private resolveKeyPath(keyPath: string): string {
    const isAbsolute = path.isAbsolute(keyPath);
    return isAbsolute ? keyPath : path.resolve(this.getProjectRoot(), keyPath);
  }

  private validateKeyPathSecurity(resolvedKeyPath: string, isProd: boolean): void {
    const allowedBases = [
      path.resolve(this.getProjectRoot(), "secrets"),
      path.resolve(process.cwd(), "secrets"),
      "/app/secrets",
    ].map(p => path.normalize(p) + path.sep);

    const normalizedKeyPath = path.normalize(resolvedKeyPath);
    const isUnderAllowedBase = allowedBases.some(base =>
      (normalizedKeyPath + path.sep).startsWith(base)
    );

    if (!isUnderAllowedBase) {
      const devMsg = `Salesforce private key must be under one of the allowed secrets directories: ${allowedBases
        .map(b => b.replace(/\\$/, ""))
        .join(", ")}. Got: ${normalizedKeyPath}`;
      throw new Error(isProd ? "Invalid Salesforce private key path" : devMsg);
    }
  }

  private async ensureKeyFileExists(resolvedKeyPath: string, isProd: boolean): Promise<void> {
    try {
      await fs.access(resolvedKeyPath);
    } catch {
      const devMsg = `Salesforce private key file not found at: ${resolvedKeyPath}. Ensure the file exists and has proper permissions (chmod 600).`;
      throw new Error(isProd ? "Salesforce private key not found" : devMsg);
    }
  }

  private createCryptoKey(
    rawPrivateKey: string,
    isProd: boolean
  ): ReturnType<typeof createPrivateKey> {
    const normalizedKey = normalizePrivateKeyInput(rawPrivateKey);
    this.validatePrivateKeyFormat(rawPrivateKey, normalizedKey, isProd);
    const cryptoKey = this.parseCryptoKey(normalizedKey, isProd);
    this.validateRsaKey(cryptoKey, isProd);
    return cryptoKey;
  }

  private validatePrivateKeyFormat(
    rawPrivateKey: string,
    normalizedKey: NormalizedKey,
    isProd: boolean
  ): void {
    const pemForValidation = normalizedKey.kind === "pem" ? normalizedKey.value : "";
    const isPkcs8 = pemForValidation.includes("BEGIN PRIVATE KEY");
    const isPkcs1Rsa = pemForValidation.includes("BEGIN RSA PRIVATE KEY");
    const containsPlaceholder =
      rawPrivateKey.includes("[PLACEHOLDER") || pemForValidation.includes("[PLACEHOLDER");

    if (containsPlaceholder || (normalizedKey.kind === "pem" && !isPkcs8 && !isPkcs1Rsa)) {
      const devMsg =
        "Salesforce private key appears to be invalid or still contains placeholder content. Expected a PEM key containing either 'BEGIN PRIVATE KEY' (PKCS8) or 'BEGIN RSA PRIVATE KEY' (PKCS1).";
      throw new Error(isProd ? INVALID_PRIVATE_KEY_MSG : devMsg);
    }
  }

  private parseCryptoKey(
    normalizedKey: NormalizedKey,
    isProd: boolean
  ): ReturnType<typeof createPrivateKey> {
    try {
      if (normalizedKey.kind === "pem") {
        return createPrivateKey({ key: normalizedKey.value, format: "pem" });
      }
      return this.parseDerKey(normalizedKey.value);
    } catch {
      const devMsg =
        "Salesforce private key could not be parsed. Ensure it is a valid RSA private key (PEM with BEGIN PRIVATE KEY / BEGIN RSA PRIVATE KEY, or base64-encoded DER).";
      throw new Error(isProd ? INVALID_PRIVATE_KEY_MSG : devMsg);
    }
  }

  private parseDerKey(base64Value: string): ReturnType<typeof createPrivateKey> {
    const der = Buffer.from(base64Value, "base64");
    try {
      return createPrivateKey({ key: der, format: "der", type: "pkcs8" });
    } catch {
      return createPrivateKey({ key: der, format: "der", type: "pkcs1" });
    }
  }

  private validateRsaKey(key: ReturnType<typeof createPrivateKey>, isProd: boolean): void {
    if (key.asymmetricKeyType !== "rsa") {
      const devMsg = `Salesforce private key must be RSA. Detected: ${key.asymmetricKeyType ?? "unknown"}`;
      throw new Error(isProd ? INVALID_PRIVATE_KEY_MSG : devMsg);
    }
  }

  private async createJwtAssertion(
    config: SalesforceAuthConfig,
    key: ReturnType<typeof createPrivateKey>
  ): Promise<string> {
    return new SignJWT({ sub: config.username })
      .setProtectedHeader({ alg: "RS256" })
      .setIssuer(config.clientId)
      .setAudience(config.audience)
      .setExpirationTime("5m")
      .sign(key);
  }

  private async requestAccessToken(
    options: TokenRequestOptions
  ): Promise<{ access_token: string; instance_url: string; id?: string }> {
    const { config, assertion, authTimeout, startTime, isProd } = options;
    const tokenUrl = `${config.audience}/services/oauth2/token`;

    this.logger.debug("Requesting Salesforce access token", {
      tokenUrl: isProd ? REDACTED : tokenUrl,
      clientId: isProd ? REDACTED : config.clientId,
      username: isProd ? REDACTED : config.username,
    });

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), authTimeout);

    try {
      const res = await fetch(tokenUrl, {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams({
          grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
          assertion,
        }),
        signal: controller.signal,
      });

      return this.handleTokenResponse(res, isProd);
    } catch (error) {
      if (error instanceof Error && error.name === "AbortError") {
        this.handleAuthTimeout(tokenUrl, authTimeout, startTime, isProd);
      }
      throw error;
    } finally {
      clearTimeout(timeoutId);
    }
  }

  private handleAuthTimeout(
    tokenUrl: string,
    authTimeout: number,
    startTime: number,
    isProd: boolean
  ): never {
    const authDuration = Date.now() - startTime;
    this.logger.error("Salesforce authentication timeout", {
      authTimeout,
      authDuration,
      tokenUrl: isProd ? REDACTED : tokenUrl,
    });
    throw new Error(
      isProd ? "Salesforce authentication timeout" : `Authentication timeout after ${authTimeout}ms`
    );
  }

  private async handleTokenResponse(
    res: Response,
    isProd: boolean
  ): Promise<{ access_token: string; instance_url: string; id?: string }> {
    if (!res.ok) {
      const errorText = await res.text();
      const logMsg = `Token request failed: ${res.status}` + (isProd ? "" : ` - ${errorText}`);
      this.logger.error(logMsg);
      throw new Error(
        isProd
          ? "Salesforce authentication failed"
          : `Token request failed: ${res.status} - ${errorText}`
      );
    }

    return res.json() as Promise<{ access_token: string; instance_url: string; id?: string }>;
  }

  private processTokenResponse(
    tokenResponse: { access_token: string; instance_url: string; id?: string },
    startTime: number,
    isProd: boolean
  ): void {
    this.connection.accessToken = tokenResponse.access_token;
    this.connection.instanceUrl = tokenResponse.instance_url;

    if (tokenResponse.id) {
      const idMatch = tokenResponse.id.match(/\/id\/([^/]+)\//);
      if (idMatch?.[1]) {
        this.organizationId = idMatch[1];
      }
    }

    const tokenTtlMs = this.getTokenTtl();
    const issuedAt = Date.now();
    this.tokenIssuedAt = issuedAt;
    this.tokenExpiresAt = issuedAt + tokenTtlMs;

    const authDuration = issuedAt - startTime;
    this.logger.log("Salesforce connection established", {
      authDuration,
      tokenTtlMs,
      expiresAt: new Date(this.tokenExpiresAt).toISOString(),
      instanceUrl: isProd ? REDACTED : this.connection.instanceUrl,
    });
  }

  private handleAuthError(error: unknown, startTime: number, isProd: boolean): void {
    const authDuration = Date.now() - startTime;
    const message = extractErrorMessage(error);

    if (isProd) {
      this.logger.error("Failed to connect to Salesforce", {
        authDuration,
        errorType: error instanceof Error ? error.constructor.name : "Unknown",
      });
    } else {
      this.logger.error(`Failed to connect to Salesforce: ${message}`, {
        authDuration,
        errorType: error instanceof Error ? error.constructor.name : "Unknown",
        errorMessage: message,
      });
    }
  }

  // Expose connection methods with automatic re-authentication
  async query(soql: string, options: { label?: string } = {}): Promise<unknown> {
    const priority = this.getQueryPriority(soql);
    const isLongRunning = this.isLongRunningQuery(soql);
    const label = options.label ?? this.deriveQueryLabel(soql);

    return this.requestQueue.execute(
      async () => {
        await this.ensureConnected();
        try {
          return await this.connection.query(soql);
        } catch (error: unknown) {
          if (this.isSessionExpiredError(error)) {
            const reAuthStartTime = Date.now();
            this.logger.warn("Salesforce session expired, attempting to re-authenticate", {
              originalError: extractErrorMessage(error),
              tokenIssuedAt: this.tokenIssuedAt ? new Date(this.tokenIssuedAt).toISOString() : null,
              tokenExpiresAt: this.tokenExpiresAt
                ? new Date(this.tokenExpiresAt).toISOString()
                : null,
            });

            try {
              await this.connect(true);

              const reAuthDuration = Date.now() - reAuthStartTime;
              this.logger.debug("Retrying query after re-authentication", {
                reAuthDuration,
              });

              return await this.connection.query(soql);
            } catch (retryError) {
              const reAuthDuration = Date.now() - reAuthStartTime;
              this.logger.error("Failed to re-authenticate or retry query", {
                originalError: extractErrorMessage(error),
                retryError: extractErrorMessage(retryError),
                reAuthDuration,
              });
              throw retryError;
            }
          }

          throw error;
        }
      },
      { priority, isLongRunning, label }
    );
  }

  private isSessionExpiredError(error: unknown): boolean {
    // Check for various session expiration indicators
    const errorMessage = extractErrorMessage(error).toLowerCase();
    const errorCode =
      (error as { errorCode?: string; name?: string })?.errorCode ||
      (error as { errorCode?: string; name?: string })?.name ||
      "";

    return (
      errorCode === "INVALID_SESSION_ID" ||
      errorMessage.includes("session expired") ||
      errorMessage.includes("invalid session") ||
      errorMessage.includes("invalid_session_id") ||
      ((error as { status?: number })?.status === 401 && errorMessage.includes("unauthorized"))
    );
  }

  sobject(type: string): SalesforceSObjectApi {
    const originalSObject = this.connection.sobject(type);

    // Return a wrapper that handles session expiration for SObject operations
    return {
      create: async (data: object) => {
        return this.requestQueue.executeHighPriority(
          async () => {
            try {
              await this.ensureConnected();
              return await originalSObject.create(data);
            } catch (error: unknown) {
              if (this.isSessionExpiredError(error)) {
                const reAuthStartTime = Date.now();
                this.logger.warn(
                  "Salesforce session expired during SObject create, attempting to re-authenticate",
                  {
                    sobjectType: type,
                    originalError: extractErrorMessage(error),
                  }
                );

                try {
                  await this.connect(true);
                  const reAuthDuration = Date.now() - reAuthStartTime;
                  this.logger.debug("Retrying SObject create after re-authentication", {
                    sobjectType: type,
                    reAuthDuration,
                  });

                  const newSObject = this.connection.sobject(type);
                  return await newSObject.create(data);
                } catch (retryError) {
                  const reAuthDuration = Date.now() - reAuthStartTime;
                  this.logger.error("Failed to re-authenticate or retry SObject create", {
                    sobjectType: type,
                    originalError: extractErrorMessage(error),
                    retryError: extractErrorMessage(retryError),
                    reAuthDuration,
                  });
                  throw retryError;
                }
              }
              throw error;
            }
          },
          { label: `salesforce:sobject:${type}:create` }
        );
      },

      update: async (data: object & { Id: string }) => {
        return this.requestQueue.executeHighPriority(
          async () => {
            try {
              await this.ensureConnected();
              return await originalSObject.update(data);
            } catch (error: unknown) {
              if (this.isSessionExpiredError(error)) {
                const reAuthStartTime = Date.now();
                this.logger.warn(
                  "Salesforce session expired during SObject update, attempting to re-authenticate",
                  {
                    sobjectType: type,
                    recordId: data.Id,
                    originalError: extractErrorMessage(error),
                  }
                );

                try {
                  await this.connect(true);
                  const reAuthDuration = Date.now() - reAuthStartTime;
                  this.logger.debug("Retrying SObject update after re-authentication", {
                    sobjectType: type,
                    recordId: data.Id,
                    reAuthDuration,
                  });

                  const newSObject = this.connection.sobject(type);
                  return await newSObject.update(data);
                } catch (retryError) {
                  const reAuthDuration = Date.now() - reAuthStartTime;
                  this.logger.error("Failed to re-authenticate or retry SObject update", {
                    sobjectType: type,
                    recordId: data.Id,
                    originalError: extractErrorMessage(error),
                    retryError: extractErrorMessage(retryError),
                    reAuthDuration,
                  });
                  throw retryError;
                }
              }
              throw error;
            }
          },
          { label: `salesforce:sobject:${type}:update` }
        );
      },
    };
  }

  async compositeTree<T>(
    sobjectType: string,
    body: unknown,
    options: { allOrNone?: boolean; priority?: number; label?: string } = {}
  ): Promise<T> {
    const priority = options.priority ?? 5;
    const allOrNone = options.allOrNone ?? true;
    const path = this.buildCompositeTreePath(sobjectType, allOrNone);
    const label = options.label ?? `salesforce:composite:${sobjectType}`;

    return this.requestQueue.execute(
      async () => {
        await this.ensureConnected();

        if (!body || typeof body !== "object") {
          throw new TypeError("Salesforce composite tree body must be an object");
        }

        const payload = body as Record<string, unknown> | Record<string, unknown>[];

        try {
          return (await this.connection.requestPost(path, payload)) as T;
        } catch (error) {
          if (this.isSessionExpiredError(error)) {
            const reAuthStartTime = Date.now();
            this.logger.warn("Salesforce session expired during composite tree request, retrying", {
              sobjectType,
              originalError: extractErrorMessage(error),
            });

            await this.connect(true);
            const reAuthDuration = Date.now() - reAuthStartTime;

            this.logger.debug("Retrying composite tree request after re-authentication", {
              sobjectType,
              reAuthDuration,
            });

            return (await this.connection.requestPost(path, payload)) as T;
          }

          throw error;
        }
      },
      { priority, label }
    );
  }

  private buildCompositeTreePath(sobjectType: string, allOrNone: boolean): string {
    const version = this.connection.version || "58.0";
    const normalizedVersion = version.startsWith("v") ? version.slice(1) : version;
    const encodedType = encodeURIComponent(sobjectType);
    const query = `?allOrNone=${allOrNone ? "true" : "false"}`;
    return `/services/data/v${normalizedVersion}/composite/tree/${encodedType}${query}`;
  }

  private deriveQueryLabel(soql: string): string {
    const match = /from\s+([a-z0-9_]+)/i.exec(soql);
    if (!match) {
      return "salesforce:query:unknown";
    }
    return `salesforce:query:${match[1]}`;
  }

  isConnected(): boolean {
    return !!this.connection.accessToken;
  }

  /**
   * Get the Salesforce Organization ID (extracted from OAuth identity URL during authentication)
   */
  getOrganizationId(): string | null {
    return this.organizationId;
  }

  private getTokenTtl(): number {
    const configured = Number(this.configService.get("SF_TOKEN_TTL_MS"));
    if (Number.isFinite(configured) && configured > 0) {
      return configured;
    }
    // Default to 12 minutes (tokens typically expire after 15 minutes)
    return 12 * 60 * 1000;
  }

  private getRefreshBuffer(): number {
    const configured = Number(this.configService.get("SF_TOKEN_REFRESH_BUFFER_MS"));
    if (Number.isFinite(configured) && configured >= 0) {
      return configured;
    }
    return 60 * 1000; // 1 minute buffer
  }

  private isTokenExpiring(): boolean {
    if (!this.tokenExpiresAt) {
      return true;
    }
    return Date.now() + this.getRefreshBuffer() >= this.tokenExpiresAt;
  }

  async ensureConnected(): Promise<void> {
    const wasConnected = this.isConnected();
    const isExpiring = this.isTokenExpiring();

    if (!wasConnected || isExpiring) {
      this.logger.debug("Salesforce connection stale; refreshing access token", {
        wasConnected,
        isExpiring,
        tokenIssuedAt: this.tokenIssuedAt ? new Date(this.tokenIssuedAt).toISOString() : null,
        tokenExpiresAt: this.tokenExpiresAt ? new Date(this.tokenExpiresAt).toISOString() : null,
        currentTime: new Date().toISOString(),
      });

      await this.connect(!wasConnected);
    }
  }

  /**
   * Execute a high-priority Salesforce request (jumps queue)
   */
  async queryHighPriority(soql: string, options: { label?: string } = {}): Promise<unknown> {
    const label = options.label ?? `${this.deriveQueryLabel(soql)}:high`;
    return this.requestQueue.executeHighPriority(
      async () => {
        await this.ensureConnected();
        try {
          return await this.connection.query(soql);
        } catch (error: unknown) {
          if (this.isSessionExpiredError(error)) {
            const reAuthStartTime = Date.now();
            this.logger.warn(
              "Salesforce session expired during high-priority query, attempting to re-authenticate",
              {
                originalError: extractErrorMessage(error),
              }
            );

            await this.connect(true);
            const reAuthDuration = Date.now() - reAuthStartTime;
            this.logger.debug("Retrying high-priority query after re-authentication", {
              reAuthDuration,
            });

            return await this.connection.query(soql);
          }
          throw error;
        }
      },
      { label }
    );
  }

  /**
   * Get queue metrics for monitoring
   */
  getQueueMetrics() {
    return this.requestQueue.getMetrics();
  }

  /**
   * Get queue health status
   */
  getQueueHealth() {
    return this.requestQueue.getHealthStatus();
  }

  /**
   * Get daily API usage information
   */
  getDailyUsage() {
    return this.requestQueue.getDailyUsage();
  }

  /**
   * Determine query priority based on SOQL content
   */
  private getQueryPriority(soql: string): number {
    const lowerSoql = soql.toLowerCase();

    // High priority queries (critical for user experience)
    if (
      lowerSoql.includes("account") ||
      lowerSoql.includes("user") ||
      lowerSoql.includes("where id =")
    ) {
      return 8;
    }

    // Medium priority queries
    if (
      lowerSoql.includes("order") ||
      lowerSoql.includes("invoice") ||
      lowerSoql.includes("limit 1")
    ) {
      return 5;
    }

    // Low priority (bulk queries, reports)
    return 2;
  }

  /**
   * Determine if query is long-running based on SOQL content
   */
  private isLongRunningQuery(soql: string): boolean {
    const lowerSoql = soql.toLowerCase();

    // Queries likely to take >20 seconds
    return (
      lowerSoql.includes("count(") ||
      lowerSoql.includes("group by") ||
      lowerSoql.includes("order by") ||
      (lowerSoql.includes("limit") && !lowerSoql.includes("limit 1")) ||
      lowerSoql.length > 500 // Very complex queries
    );
  }
}
