import { getCollection } from '../../lib/datastore.js';
import type { IPRuleDoc } from './types.js';

function collection() {
  return getCollection<IPRuleDoc>('ip_rules', '/productId');
}

export async function listRules(productId: string): Promise<IPRuleDoc[]> {
  return collection().findMany({
    filter: { productId },
    sort: { createdAt: -1 },
  });
}

export async function getActiveRules(productId: string): Promise<IPRuleDoc[]> {
  // Fetch all rules, then filter in-memory for the OR condition
  // (expiresAt not defined OR expiresAt > now)
  const allRules = await collection().findMany({
    filter: { productId },
  });
  const now = new Date().toISOString();
  return allRules.filter(r => !r.expiresAt || r.expiresAt > now);
}

export async function createRule(doc: IPRuleDoc): Promise<IPRuleDoc> {
  return collection().create(doc);
}

export async function deleteRule(id: string, productId: string): Promise<boolean> {
  try {
    await collection().delete(id, productId);
    return true;
  } catch {
    return false;
  }
}

/**
 * Check if an IP is explicitly allowed or denied.
 * Returns 'allow', 'deny', or null (no matching rule).
 */
export async function checkIP(ip: string, productId: string): Promise<'allow' | 'deny' | null> {
  const rules = await getActiveRules(productId);

  for (const rule of rules) {
    if (rule.ip === ip || matchesCIDR(ip, rule.ip)) {
      return rule.action;
    }
  }

  return null;
}

/**
 * Basic CIDR matching for IPv4.
 * Supports exact match and /prefix notation (e.g., "10.0.0.0/8").
 */
function matchesCIDR(ip: string, cidr: string): boolean {
  if (!cidr.includes('/')) return ip === cidr;

  const [base, prefixStr] = cidr.split('/');
  const prefix = parseInt(prefixStr, 10);
  if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;

  const ipNum = ipToNumber(ip);
  const baseNum = ipToNumber(base);
  if (ipNum === null || baseNum === null) return false;

  const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;
  return (ipNum & mask) === (baseNum & mask);
}

function ipToNumber(ip: string): number | null {
  const parts = ip.split('.');
  if (parts.length !== 4) return null;

  let num = 0;
  for (const part of parts) {
    const octet = parseInt(part, 10);
    if (isNaN(octet) || octet < 0 || octet > 255) return null;
    num = (num << 8) | octet;
  }
  return num >>> 0;
}
