/**
 * meSEO Logger
 * ─────────────
 * Simple logging that writes to both console and database.
 *
 * How it works:
 *   1. Always logs to console (for local dev / hosting logs)
 *   2. Saves to the AppLog table in the database (for the admin UI later)
 *   3. DB writes are fire-and-forget — logging never blocks or crashes the app
 *
 * Usage:
 *   import { log } from "@/lib/logger";
 *
 *   log.info("api", "User loaded dashboard", { userId: "...", path: "/api/dashboard" });
 *   log.warn("collect", "Domain mismatch", { domain: "evil.com", siteId: "ms_..." });
 *   log.error("integration", "GSC sync failed", { error: err.message });
 *   log.apiRequest("/api/billing", "GET", 200, 42, { userId: "..." });
 */

import { prisma } from "@/lib/prisma";
import { env } from "@/lib/env";

// ─── Types ───────────────────────────────────────────────────────────────────

type LogLevel = "info" | "warn" | "error";
type LogSource = "api" | "collect" | "integration" | "billing" | "avatar" | "auth" | "system";

// ─── Console formatting ─────────────────────────────────────────────────────

const LEVEL_PREFIX: Record<LogLevel, string> = {
  info:  "[INFO]",
  warn:  "[WARN]",
  error: "[ERROR]",
};

function toConsole(level: LogLevel, source: string, message: string, meta?: Record<string, unknown>) {
  const prefix = `${LEVEL_PREFIX[level]} [${source}]`;
  const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";

  if (level === "error") {
    console.error(`${prefix} ${message}${metaStr}`);
  } else if (level === "warn") {
    console.warn(`${prefix} ${message}${metaStr}`);
  } else if (env.features.debugMode) {
    // Only log info in debug mode to avoid noise in production
    console.log(`${prefix} ${message}${metaStr}`);
  }
}

// ─── Database write (fire-and-forget) ────────────────────────────────────────

function toDB(
  level: LogLevel,
  source: string,
  message: string,
  path?: string,
  userId?: string,
  metadata?: Record<string, unknown>
) {
  // Fire and forget — never await, never throw
  prisma.appLog.create({
    data: {
      level,
      source,
      message,
      path: path ?? null,
      userId: userId ?? null,
      metadata: metadata ? JSON.parse(JSON.stringify(metadata)) : {},
    },
  }).catch(() => {
    // Silently ignore DB write failures — logging should never break the app
  });
}

// ─── Core log function ───────────────────────────────────────────────────────

function write(
  level: LogLevel,
  source: LogSource,
  message: string,
  options?: { path?: string; userId?: string; metadata?: Record<string, unknown> }
) {
  const { path, userId, metadata } = options ?? {};

  // Always write to console
  toConsole(level, source, message, metadata);

  // Write to DB (errors always, others only when not in dev to avoid noise)
  if (level === "error" || level === "warn" || !env.isDev) {
    toDB(level, source, message, path, userId, metadata);
  }
}

// ─── Public API ──────────────────────────────────────────────────────────────

export const log = {
  /**
   * Info log — normal operations, request completed, etc.
   */
  info(source: LogSource, message: string, options?: { path?: string; userId?: string; metadata?: Record<string, unknown> }) {
    write("info", source, message, options);
  },

  /**
   * Warning — something unexpected but not broken (domain mismatch, rate limit, etc.)
   */
  warn(source: LogSource, message: string, options?: { path?: string; userId?: string; metadata?: Record<string, unknown> }) {
    write("warn", source, message, options);
  },

  /**
   * Error — something failed (DB error, integration failure, unhandled exception)
   */
  error(source: LogSource, message: string, options?: { path?: string; userId?: string; metadata?: Record<string, unknown> }) {
    write("error", source, message, options);
  },

  /**
   * Convenience: log an API request with timing.
   * Call at the end of a route handler.
   *
   * Example:
   *   const start = Date.now();
   *   // ... handle request ...
   *   log.apiRequest("/api/billing", "GET", 200, Date.now() - start, { userId });
   */
  apiRequest(path: string, method: string, status: number, durationMs: number, extra?: { userId?: string; metadata?: Record<string, unknown> }) {
    const level: LogLevel = status >= 500 ? "error" : status >= 400 ? "warn" : "info";
    write(level, "api", `${method} ${path} → ${status} (${durationMs}ms)`, {
      path,
      userId: extra?.userId,
      metadata: { method, status, durationMs, ...extra?.metadata },
    });
  },

  /**
   * Convenience: log a tracking event from /api/collect.
   */
  trackingEvent(siteId: string, eventType: string, pageUrl: string, result: "ok" | "rejected", reason?: string) {
    const level: LogLevel = result === "ok" ? "info" : "warn";
    write(level, "collect", `${result === "ok" ? "Accepted" : "Rejected"}: ${eventType} from ${siteId}`, {
      path: "/api/collect",
      metadata: { siteId, eventType, pageUrl, result, reason },
    });
  },

  /**
   * Convenience: log an integration sync result.
   */
  integrationSync(service: string, result: "success" | "failed", detail?: string) {
    const level: LogLevel = result === "success" ? "info" : "error";
    write(level, "integration", `${service} sync ${result}${detail ? ": " + detail : ""}`, {
      metadata: { service, result, detail },
    });
  },
};

// ─── Log cleanup ─────────────────────────────────────────────────────────────

/**
 * Delete logs older than the given number of days.
 * Call this on a schedule (e.g. daily cron) to prevent the table from growing forever.
 */
export async function pruneOldLogs(daysToKeep: number = 30): Promise<number> {
  const cutoff = new Date();
  cutoff.setDate(cutoff.getDate() - daysToKeep);

  const result = await prisma.appLog.deleteMany({
    where: { createdAt: { lt: cutoff } },
  });

  return result.count;
}
