/**
 * Centralized Logging System
 *
 * Replaces scattered console.log usage with structured, configurable logging.
 * Integrates with the centralized Config system for environment-aware behavior.
 */

import { ConfigHelpers } from "@/config/Config.js";

export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
export type LogContext = Record<string, unknown>;

export interface LogEntry {
  readonly timestamp: string;
  readonly level: LogLevel;
  readonly message: string;
  readonly context?: LogContext | undefined;
  readonly component?: string | undefined;
  readonly requestId?: string | undefined;
  readonly siteId?: string | undefined;
  readonly userId?: string | undefined;
}

export interface LoggerOptions {
  readonly component?: string | undefined;
  readonly context?: LogContext | undefined;
  readonly siteId?: string | undefined;
  readonly requestId?: string | undefined;
}

/**
 * Log level priorities (higher number = more important)
 */
const LOG_LEVELS: Record<LogLevel, number> = {
  trace: 0,
  debug: 1,
  info: 2,
  warn: 3,
  error: 4,
  fatal: 5,
};

/**
 * Get minimum log level from configuration
 */
function getMinLogLevel(): LogLevel {
  try {
    const configInstance = ConfigHelpers.get();
    const appConfig = configInstance.get();
    const configLevel = appConfig.debug.logLevel.toLowerCase();
    return LOG_LEVELS[configLevel as LogLevel] !== undefined ? (configLevel as LogLevel) : "info";
  } catch {
    return "info";
  }
}

/**
 * Check if log level should be output
 */
function shouldLog(level: LogLevel): boolean {
  const minLevel = getMinLogLevel();
  return LOG_LEVELS[level] >= LOG_LEVELS[minLevel];
}

/**
 * Format log entry for output
 */
function formatLogEntry(entry: LogEntry): string {
  const timestamp = entry.timestamp;
  const level = entry.level.toUpperCase().padEnd(5);
  const component = entry.component ? `[${entry.component}]` : "";
  const siteId = entry.siteId ? `{site:${entry.siteId}}` : "";
  const requestId = entry.requestId ? `{req:${entry.requestId.slice(0, 8)}}` : "";

  let message = `${timestamp} ${level} ${component}${siteId}${requestId} ${entry.message}`;

  if (entry.context && Object.keys(entry.context).length > 0) {
    const contextStr = JSON.stringify(entry.context);
    message += ` ${contextStr}`;
  }

  return message;
}

/**
 * Sanitize sensitive data from log context
 */
function sanitizeContext(context: LogContext): LogContext {
  const sanitized: LogContext = {};

  for (const [key, value] of Object.entries(context)) {
    const keyLower = key.toLowerCase();
    const isSensitive =
      keyLower.includes("password") ||
      keyLower.includes("secret") ||
      keyLower.includes("token") ||
      keyLower.includes("key") ||
      keyLower.includes("credential");

    if (isSensitive) {
      if (typeof value === "string") {
        sanitized[key] = value.length > 0 ? `[REDACTED:${value.length}chars]` : "[EMPTY]";
      } else if (Array.isArray(value)) {
        sanitized[key] = "[EMPTY]"; // Redact entire array for sensitive fields
      } else {
        sanitized[key] = value; // Keep non-string, non-array values as-is
      }
    } else {
      sanitized[key] = value;
    }
  }

  return sanitized;
}

/**
 * Output log entry to appropriate destination
 */
function outputLog(entry: LogEntry): void {
  try {
    // In test environment, only log errors and fatals
    if (ConfigHelpers.isTest() && LOG_LEVELS[entry.level] < LOG_LEVELS.error) {
      return;
    }

    // In DXT mode, suppress most logging
    if (ConfigHelpers.isDXT() && LOG_LEVELS[entry.level] < LOG_LEVELS.warn) {
      return;
    }
  } catch {
    // Config not available — fall through to output
  }

  const formatted = formatLogEntry(entry);

  // Use stderr for all log output to avoid STDIO interference
  console.error(formatted);
}

/**
 * Main Logger Class
 */
export class Logger {
  private readonly options: LoggerOptions;

  constructor(options: LoggerOptions = {}) {
    this.options = options;
  }

  /**
   * Create child logger with additional context
   */
  child(additionalOptions: LoggerOptions): Logger {
    return new Logger({
      component: additionalOptions.component ?? this.options.component,
      siteId: additionalOptions.siteId ?? this.options.siteId,
      requestId: additionalOptions.requestId ?? this.options.requestId,
      context: {
        ...this.options.context,
        ...additionalOptions.context,
      },
    });
  }

  /**
   * Log at specific level
   */
  log(level: LogLevel, message: string, context?: LogContext): void {
    if (!shouldLog(level)) {
      return;
    }

    const mergedContext = context ? sanitizeContext({ ...this.options.context, ...context }) : this.options.context;

    const entry: LogEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      ...(mergedContext && Object.keys(mergedContext).length > 0 && { context: mergedContext }),
      ...(this.options.component && { component: this.options.component }),
      ...(this.options.requestId && { requestId: this.options.requestId }),
      ...(this.options.siteId && { siteId: this.options.siteId }),
    };

    outputLog(entry);
  }

  // Convenience methods
  trace(message: string, context?: LogContext): void {
    this.log("trace", message, context);
  }

  debug(message: string, context?: LogContext): void {
    this.log("debug", message, context);
  }

  info(message: string, context?: LogContext): void {
    this.log("info", message, context);
  }

  warn(message: string, context?: LogContext): void {
    this.log("warn", message, context);
  }

  error(message: string, context?: LogContext): void;
  error(error: Error, context?: LogContext): void;
  error(messageOrError: string | Error, context?: LogContext): void {
    if (messageOrError instanceof Error) {
      this.log("error", messageOrError.message, {
        ...context,
        errorName: messageOrError.name,
        errorStack: messageOrError.stack,
      });
    } else {
      this.log("error", messageOrError, context);
    }
  }

  fatal(message: string, context?: LogContext): void;
  fatal(error: Error, context?: LogContext): void;
  fatal(messageOrError: string | Error, context?: LogContext): void {
    if (messageOrError instanceof Error) {
      this.log("fatal", messageOrError.message, {
        ...context,
        errorName: messageOrError.name,
        errorStack: messageOrError.stack,
      });
    } else {
      this.log("fatal", messageOrError, context);
    }
  }

  /**
   * Time a function execution
   */
  time<T>(message: string, fn: () => T): T;
  time<T>(message: string, fn: () => Promise<T>): Promise<T>;
  time<T>(message: string, fn: () => T | Promise<T>): T | Promise<T> {
    const start = Date.now();
    this.debug(`Starting: ${message}`);

    try {
      const result = fn();

      // Check if result is a Promise by checking for then method
      if (result && typeof result === "object" && "then" in result) {
        // Cast to Promise<T> since we know it has a then method
        const promiseResult = result as Promise<T>;
        return promiseResult
          .then((value: T) => {
            const duration = Date.now() - start;
            this.debug(`Completed: ${message}`, { duration: `${duration}ms` });
            return value;
          })
          .catch((error: unknown) => {
            const duration = Date.now() - start;
            this.error(`Failed: ${message}`, {
              duration: `${duration}ms`,
              error: error instanceof Error ? error.message : String(error),
            });
            throw error;
          });
      } else {
        const duration = Date.now() - start;
        this.debug(`Completed: ${message}`, { duration: `${duration}ms` });
        return result;
      }
    } catch (_error) {
      const duration = Date.now() - start;
      this.error(`Failed: ${message}`, {
        duration: `${duration}ms`,
        _error: _error instanceof Error ? _error.message : String(_error),
      });
      throw _error;
    }
  }
}

/**
 * Default logger instance
 */
export const logger = new Logger();

/**
 * Create component-specific logger
 */
export function createLogger(component: string, options: Omit<LoggerOptions, "component"> = {}): Logger {
  return new Logger({ ...options, component });
}

/**
 * Create site-specific logger
 */
export function createSiteLogger(siteId: string, component?: string | undefined): Logger {
  return new Logger({
    siteId,
    ...(component && { component }),
  });
}

/**
 * Create request-specific logger
 */
export function createRequestLogger(
  requestId: string,
  component?: string | undefined,
  siteId?: string | undefined,
): Logger {
  return new Logger({
    requestId,
    ...(component && { component }),
    ...(siteId && { siteId }),
  });
}

/**
 * Logger factory for common scenarios
 */
export const LoggerFactory = {
  /**
   * Create logger for WordPress API operations
   */
  api: (siteId?: string | undefined) => createLogger("API", siteId ? { siteId } : {}),

  /**
   * Create logger for cache operations
   */
  cache: (siteId?: string | undefined) => createLogger("CACHE", siteId ? { siteId } : {}),

  /**
   * Create logger for tool operations
   */
  tool: (toolName: string, siteId?: string | undefined) => createLogger(`TOOL:${toolName}`, siteId ? { siteId } : {}),

  /**
   * Create logger for authentication
   */
  auth: (siteId?: string | undefined) => createLogger("AUTH", siteId ? { siteId } : {}),

  /**
   * Create logger for configuration
   */
  config: () => createLogger("CONFIG"),

  /**
   * Create logger for security operations
   */
  security: () => createLogger("SECURITY"),

  /**
   * Create logger for performance monitoring
   */
  performance: () => createLogger("PERF"),

  /**
   * Create logger for server operations
   */
  server: () => createLogger("SERVER"),

  /**
   * Create logger for client-layer operations
   */
  client: (component?: string | undefined, siteId?: string | undefined) =>
    createLogger(component || "CLIENT", siteId ? { siteId } : {}),
};

/**
 * Performance measurement utility
 * Re-exported here so client code can import from a single module.
 */
export interface PerformanceTimer {
  end(): number;
}

export const startTimer = (): PerformanceTimer => {
  const start = Date.now();
  return {
    end(): number {
      return Date.now() - start;
    },
  };
};
