import { randomBytes } from "crypto";
import logger from "./logger.js";
import pool from "./db.js";

export interface ApiKey {
  key: string;
  tier: "free" | "pro";
  email: string;
  createdAt: string;
  stripeCustomerId?: string;
}

// In-memory cache for fast lookups, synced with PostgreSQL
let keysCache: ApiKey[] = [];

export async function loadKeys(): Promise<void> {
  try {
    const result = await pool.query(
      "SELECT key, tier, email, created_at, stripe_customer_id FROM api_keys"
    );
    keysCache = result.rows.map((r) => ({
      key: r.key,
      tier: r.tier as "free" | "pro",
      email: r.email,
      createdAt: r.created_at instanceof Date ? r.created_at.toISOString() : r.created_at,
      stripeCustomerId: r.stripe_customer_id || undefined,
    }));
  } catch (err) {
    logger.error({ err }, "Failed to load keys from PostgreSQL");
    keysCache = [];
  }

  // Also load seed keys from env
  const envKeys = process.env.API_KEYS?.split(",").map((k) => k.trim()).filter(Boolean) || [];
  for (const k of envKeys) {
    if (!keysCache.find((e) => e.key === k)) {
      const entry: ApiKey = { key: k, tier: "pro", email: "seed@docfast.dev", createdAt: new Date().toISOString() };
      keysCache.push(entry);
      // Upsert into DB
      await pool.query(
        `INSERT INTO api_keys (key, tier, email, created_at) VALUES ($1, $2, $3, $4)
         ON CONFLICT (key) DO NOTHING`,
        [k, "pro", "seed@docfast.dev", new Date().toISOString()]
      ).catch(() => {});
    }
  }
}

export function isValidKey(key: string): boolean {
  return keysCache.some((k) => k.key === key);
}

export function getKeyInfo(key: string): ApiKey | undefined {
  return keysCache.find((k) => k.key === key);
}

export function isProKey(key: string): boolean {
  const info = getKeyInfo(key);
  return info?.tier === "pro";
}

function generateKey(prefix: string): string {
  return `${prefix}_${randomBytes(24).toString("hex")}`;
}

export async function createFreeKey(email?: string): Promise<ApiKey> {
  if (email) {
    const existing = keysCache.find((k) => k.email === email && k.tier === "free");
    if (existing) return existing;
  }

  const entry: ApiKey = {
    key: generateKey("df_free"),
    tier: "free",
    email: email || "",
    createdAt: new Date().toISOString(),
  };

  await pool.query(
    "INSERT INTO api_keys (key, tier, email, created_at) VALUES ($1, $2, $3, $4)",
    [entry.key, entry.tier, entry.email, entry.createdAt]
  );
  keysCache.push(entry);
  return entry;
}

export async function createProKey(email: string, stripeCustomerId: string): Promise<ApiKey> {
  const existing = keysCache.find((k) => k.stripeCustomerId === stripeCustomerId);
  if (existing) {
    existing.tier = "pro";
    await pool.query("UPDATE api_keys SET tier = 'pro' WHERE key = $1", [existing.key]);
    return existing;
  }

  const entry: ApiKey = {
    key: generateKey("df_pro"),
    tier: "pro",
    email,
    createdAt: new Date().toISOString(),
    stripeCustomerId,
  };

  await pool.query(
    "INSERT INTO api_keys (key, tier, email, created_at, stripe_customer_id) VALUES ($1, $2, $3, $4, $5)",
    [entry.key, entry.tier, entry.email, entry.createdAt, entry.stripeCustomerId]
  );
  keysCache.push(entry);
  return entry;
}

export async function downgradeByCustomer(stripeCustomerId: string): Promise<boolean> {
  const entry = keysCache.find((k) => k.stripeCustomerId === stripeCustomerId);
  if (entry) {
    entry.tier = "free";
    await pool.query("UPDATE api_keys SET tier = 'free' WHERE stripe_customer_id = $1", [stripeCustomerId]);
    return true;
  }
  return false;
}

export function getAllKeys(): ApiKey[] {
  return [...keysCache];
}

export async function updateKeyEmail(apiKey: string, newEmail: string): Promise<boolean> {
  const entry = keysCache.find((k) => k.key === apiKey);
  if (!entry) return false;
  entry.email = newEmail;
  await pool.query("UPDATE api_keys SET email = $1 WHERE key = $2", [newEmail, apiKey]);
  return true;
}

export async function updateEmailByCustomer(stripeCustomerId: string, newEmail: string): Promise<boolean> {
  const entry = keysCache.find(k => k.stripeCustomerId === stripeCustomerId);
  if (!entry) return false;
  entry.email = newEmail;
  await pool.query("UPDATE api_keys SET email = $1 WHERE stripe_customer_id = $2", [newEmail, stripeCustomerId]);
  return true;
}
