/**
 * API Key Groups DB — CRUD operations for team/enterprise key grouping
 *
 * Tables: key_groups, group_model_permissions, key_group_members
 * Migration: 065_api_key_groups.sql
 *
 * Enables team-level API key management with model-level access control.
 */

import { getDbInstance } from "@/lib/db/core";
import { randomUUID } from "crypto";

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

export interface KeyGroup {
  id: string;
  name: string;
  description: string;
  isActive: boolean;
  createdAt: string;
  updatedAt: string;
}

export interface GroupModelPermission {
  id: string;
  groupId: string;
  modelPattern: string;
  provider: string | null;
  accessType: "allow" | "deny";
  createdAt: string;
}

export interface KeyGroupMember {
  keyId: string;
  groupId: string;
  createdAt: string;
}

export interface KeyGroupWithPermissions extends KeyGroup {
  permissions: GroupModelPermission[];
  memberCount: number;
}

// ── Key Groups CRUD ──────────────────────────────────────────────────────

export function getAllKeyGroups(): KeyGroup[] {
  const db = getDbInstance() as any;
  const rows = db.prepare("SELECT * FROM key_groups ORDER BY name ASC").all() as any[];
  return rows.map(rowToGroup);
}

export function getKeyGroup(id: string): KeyGroup | undefined {
  const db = getDbInstance() as any;
  const row = db.prepare("SELECT * FROM key_groups WHERE id = ?").get(id) as any;
  return row ? rowToGroup(row) : undefined;
}

export function getKeyGroupWithPermissions(id: string): KeyGroupWithPermissions | undefined {
  const group = getKeyGroup(id);
  if (!group) return undefined;

  const permissions = getGroupPermissions(id);
  const memberCount = getGroupMemberCount(id);

  return { ...group, permissions, memberCount };
}

export function createKeyGroup(name: string, description = ""): KeyGroup {
  const db = getDbInstance() as any;
  const id = randomUUID();
  const now = new Date().toISOString();

  db.prepare(
    "INSERT INTO key_groups (id, name, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
  ).run(id, name, description, now, now);

  return getKeyGroup(id)!;
}

export function updateKeyGroup(
  id: string,
  updates: { name?: string; description?: string; isActive?: boolean }
): KeyGroup | undefined {
  const existing = getKeyGroup(id);
  if (!existing) return undefined;

  const db = getDbInstance() as any;
  const sets: string[] = [];
  const params: Record<string, unknown> = { id };

  if (updates.name !== undefined) {
    sets.push("name = @name");
    params.name = updates.name;
  }
  if (updates.description !== undefined) {
    sets.push("description = @description");
    params.description = updates.description;
  }
  if (updates.isActive !== undefined) {
    sets.push("is_active = @isActive");
    params.isActive = updates.isActive ? 1 : 0;
  }

  if (sets.length === 0) return existing;
  sets.push("updated_at = datetime('now')");

  db.prepare(`UPDATE key_groups SET ${sets.join(", ")} WHERE id = @id`).run(params);
  return getKeyGroup(id);
}

export function deleteKeyGroup(id: string): boolean {
  const db = getDbInstance() as any;
  // CASCADE deletes permissions and members
  const result = db.prepare("DELETE FROM key_groups WHERE id = ?").run(id);
  return result.changes > 0;
}

// ── Group Permissions ────────────────────────────────────────────────────

export function getGroupPermissions(groupId: string): GroupModelPermission[] {
  const db = getDbInstance() as any;
  const rows = db
    .prepare(
      "SELECT * FROM group_model_permissions WHERE group_id = ? ORDER BY access_type ASC, model_pattern ASC"
    )
    .all(groupId) as any[];
  return rows.map(rowToPermission);
}

export function addGroupPermission(
  groupId: string,
  modelPattern: string,
  accessType: "allow" | "deny",
  provider?: string
): GroupModelPermission {
  const db = getDbInstance() as any;
  const id = randomUUID();
  const now = new Date().toISOString();

  db.prepare(
    "INSERT INTO group_model_permissions (id, group_id, model_pattern, provider, access_type, created_at) VALUES (?, ?, ?, ?, ?, ?)"
  ).run(id, groupId, modelPattern, provider || null, accessType, now);

  return getGroupPermissions(groupId).find((p) => p.id === id)!;
}

export function removeGroupPermission(permissionId: string): boolean {
  const db = getDbInstance() as any;
  const result = db.prepare("DELETE FROM group_model_permissions WHERE id = ?").run(permissionId);
  return result.changes > 0;
}

export function clearGroupPermissions(groupId: string): void {
  const db = getDbInstance() as any;
  db.prepare("DELETE FROM group_model_permissions WHERE group_id = ?").run(groupId);
}

// ── Key Group Members ────────────────────────────────────────────────────

export function getGroupMembers(groupId: string): KeyGroupMember[] {
  const db = getDbInstance() as any;
  const rows = db
    .prepare("SELECT * FROM key_group_members WHERE group_id = ? ORDER BY created_at ASC")
    .all(groupId) as any[];
  return rows.map(rowToMember);
}

export function getKeyGroupsForApiKey(keyId: string): KeyGroup[] {
  const db = getDbInstance() as any;
  const rows = db
    .prepare(
      `
    SELECT g.* FROM key_groups g
    INNER JOIN key_group_members m ON g.id = m.group_id
    WHERE m.key_id = ? AND g.is_active = 1
    ORDER BY g.name ASC
  `
    )
    .all(keyId) as any[];
  return rows.map(rowToGroup);
}

export function addKeyToGroup(keyId: string, groupId: string): boolean {
  const db = getDbInstance() as any;
  try {
    db.prepare("INSERT OR IGNORE INTO key_group_members (key_id, group_id) VALUES (?, ?)").run(
      keyId,
      groupId
    );
    return true;
  } catch {
    return false;
  }
}

export function removeKeyFromGroup(keyId: string, groupId: string): boolean {
  const db = getDbInstance() as any;
  const result = db
    .prepare("DELETE FROM key_group_members WHERE key_id = ? AND group_id = ?")
    .run(keyId, groupId);
  return result.changes > 0;
}

function getGroupMemberCount(groupId: string): number {
  const db = getDbInstance() as any;
  const row = db
    .prepare("SELECT COUNT(*) as count FROM key_group_members WHERE group_id = ?")
    .get(groupId) as any;
  return row?.count || 0;
}

// ── Authorization Helper ────────────────────────────────────────────────

export interface ModelAccessCheck {
  allowed: boolean;
  matchedRules: GroupModelPermission[];
  deniedBy: GroupModelPermission | null;
}

/**
 * Check if an API key has access to a specific model.
 * Deny rules override allow rules. If no rules match, access is allowed by default.
 */
export function checkKeyModelAccess(
  keyId: string,
  model: string,
  provider?: string
): ModelAccessCheck {
  const groups = getKeyGroupsForApiKey(keyId);
  if (groups.length === 0) {
    // No groups = no restrictions
    return { allowed: true, matchedRules: [], deniedBy: null };
  }

  const db = getDbInstance() as any;
  const groupIds = groups.map((g) => g.id);
  const placeholders = groupIds.map(() => "?").join(",");

  const rules = db
    .prepare(
      `
    SELECT * FROM group_model_permissions
    WHERE group_id IN (${placeholders})
    ORDER BY access_type ASC
  `
    )
    .all(...groupIds) as any[];

  const permissions = rules.map(rowToPermission);

  // Check deny rules first (they take precedence)
  const denyRules = permissions.filter(
    (p) =>
      p.accessType === "deny" &&
      matchesModelPattern(p.modelPattern, model) &&
      (!p.provider || p.provider === provider)
  );

  if (denyRules.length > 0) {
    return { allowed: false, matchedRules: permissions, deniedBy: denyRules[0] };
  }

  // Check allow rules
  const allowRules = permissions.filter(
    (p) =>
      p.accessType === "allow" &&
      matchesModelPattern(p.modelPattern, model) &&
      (!p.provider || p.provider === provider)
  );

  if (allowRules.length > 0) {
    return { allowed: true, matchedRules: permissions, deniedBy: null };
  }

  // No matching rules = restricted by group membership but no explicit allow
  return { allowed: false, matchedRules: permissions, deniedBy: null };
}

function matchesModelPattern(pattern: string, model: string): boolean {
  if (pattern === "*") return true;
  if (pattern.includes("*")) {
    const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
    return regex.test(model);
  }
  return pattern === model;
}

// ── Row Mappers ──────────────────────────────────────────────────────────

function rowToGroup(row: any): KeyGroup {
  return {
    id: row.id,
    name: row.name,
    description: row.description || "",
    isActive: row.is_active === 1,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

function rowToPermission(row: any): GroupModelPermission {
  return {
    id: row.id,
    groupId: row.group_id,
    modelPattern: row.model_pattern,
    provider: row.provider || null,
    accessType: row.access_type,
    createdAt: row.created_at,
  };
}

function rowToMember(row: any): KeyGroupMember {
  return {
    keyId: row.key_id,
    groupId: row.group_id,
    createdAt: row.created_at,
  };
}
