type LogLevel = "info" | "success" | "warn" | "error";

interface LoggerOptions {
  enabled?: boolean;
  showTimestamp?: boolean;
  collapsed?: boolean;
  colors?: boolean;
}

let cachedTimestamp = "";
let lastTimestampUpdate = 0;

function getTimestamp(): string {
  const now = Date.now();

  if (now - lastTimestampUpdate > 1000) {
    const date = new Date(now);
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");

    cachedTimestamp = `${month}/${day} ${hours}:${minutes}:${seconds}`;
    lastTimestampUpdate = now;
  }

  return cachedTimestamp;
}

function getLevelStyle(level: LogLevel): { color: string; label: string } {
  const styles = {
    info: { color: "#f5f5f5", label: "INFO" },
    success: { color: "#10B981", label: "SUCCESS" },
    warn: { color: "#F59E0B", label: "WARN" },
    error: { color: "#EF4444", label: "ERROR" },
  };

  return styles[level] || styles.info;
}

class Logger {
  private options: LoggerOptions;
  private context?: string;

  constructor(context?: string, options: LoggerOptions = {}) {
    this.context = context;
    this.options = {
      enabled: true,
      showTimestamp: true,
      collapsed: true,
      colors: true,
      ...options,
    };
  }

  child(context: string, options?: LoggerOptions): Logger {
    const childContext = this.context
      ? `${this.context} > ${context}`
      : context;
    return new Logger(childContext, { ...this.options, ...options });
  }

  private log(
    level: LogLevel,
    label: string,
    data?: any,
    ...rest: any[]
  ): void {
    if (!this.options.enabled) return;

    const style = getLevelStyle(level);
    const timestamp = this.options.showTimestamp ? `${getTimestamp()} │ ` : "";
    const context = this.context ? ` │ ${this.context}` : "";

    const groupLabel = `${timestamp}${style.label}${context} │ ${label}`;

    // In server environment (no window), use simple console.log instead of groups
    const isServer = typeof window === "undefined";

    if (isServer) {
      // Server-side: Simple formatted output (no console.group in Node.js)
      console.log(groupLabel);
      if (data !== undefined) {
        console.log(JSON.stringify(data, null, 2));
      }
      if (rest.length > 0) {
        for (const item of rest) {
          console.log(JSON.stringify(item, null, 2));
        }
      }
    } else {
      // Browser: Use console.group with colors
      const group = this.options.collapsed
        ? console.groupCollapsed
        : console.group;

      if (this.options.colors) {
        group(`%c${groupLabel}`, `color: ${style.color}; font-weight: bold;`);
      } else {
        group(groupLabel);
      }

      if (data !== undefined) {
        console.log(data);
      }

      if (rest.length > 0) {
        for (const item of rest) {
          console.log(item);
        }
      }

      console.groupEnd();
    }
  }

  info(label: string, data?: any, ...rest: any[]): void {
    this.log("info", label, data, ...rest);
  }

  success(label: string, data?: any, ...rest: any[]): void {
    this.log("success", label, data, ...rest);
  }

  warn(label: string, data?: any, ...rest: any[]): void {
    this.log("warn", label, data, ...rest);
  }

  error(label: string, data?: any, ...rest: any[]): void {
    this.log("error", label, data, ...rest);
  }

  simple(message: string): void {
    if (!this.options.enabled) return;

    const style = getLevelStyle("info");
    const timestamp = this.options.showTimestamp ? `${getTimestamp()} │ ` : "";
    const context = this.context ? ` │ ${this.context}` : "";

    const logMessage = `${timestamp}${style.label}${context} │ ${message}`;

    if (this.options.colors && typeof window !== "undefined") {
      console.log(`%c${logMessage}`, `color: ${style.color};`);
    } else {
      console.log(logMessage);
    }
  }

  async measure<T>(label: string, fn: () => Promise<T>): Promise<T> {
    const start = performance.now();

    try {
      const result = await fn();
      const duration = (performance.now() - start).toFixed(2);

      this.success(`${label} completed`, {
        duration: `${duration}ms`,
        result,
      });

      return result;
    } catch (error) {
      const duration = (performance.now() - start).toFixed(2);

      this.error(`${label} failed`, {
        duration: `${duration}ms`,
        error,
      });

      throw error;
    }
  }
}

export const logger = new Logger();

export { Logger };
