/**
 * RBAC - 基于角色的访问控制
 *
 * 表结构:
 * - admin_permission: 权限表 (id, name, code, description, created_at)
 * - admin_role: 角色表 (id, name, description, created_at)
 * - admin_role_permission: 角色-权限关联表 (role_id, permission_id)
 * - admin_user: 管理员用户表 (id, username, password_hash, is_active, created_at, updated_at)
 * - admin_user_role: 用户-角色关联表 (user_id, role_id)
 */

// ============ 类型定义 ============

export interface Permission {
  id: number;
  name: string;
  code: string;
  description: string | null;
  created_at: Date;
}

export interface Role {
  id: number;
  name: string;
  description: string | null;
  created_at: Date;
}

export interface AdminUser {
  id: number;
  username: string;
  password_hash: string;
  is_active: boolean;
  created_at: Date;
  updated_at: Date;
}

export interface AdminSession {
  userId: number;
  username: string;
  roles: string[];
  permissions: string[];
}

// ============ Permission CRUD ============

export async function listPermissions(): Promise<Permission[]> {
  const { query } = await import("./db");
  const result = await query<Permission>(
    "SELECT id, name, code, description, created_at FROM admin_permission ORDER BY id"
  );
  return result.rows;
}

export async function getPermissionById(
  id: number
): Promise<Permission | null> {
  const { query } = await import("./db");
  const result = await query<Permission>(
    "SELECT id, name, code, description, created_at FROM admin_permission WHERE id = $1",
    [id]
  );
  return result.rows[0] || null;
}

export async function createPermission(
  name: string,
  code: string,
  description?: string
): Promise<Permission> {
  const { query } = await import("./db");
  const result = await query<Permission>(
    `INSERT INTO admin_permission (name, code, description) VALUES ($1, $2, $3)
     RETURNING id, name, code, description, created_at`,
    [name, code, description || null]
  );
  return result.rows[0];
}

export async function updatePermission(
  id: number,
  name: string,
  code: string,
  description?: string
): Promise<Permission | null> {
  const { query } = await import("./db");
  const result = await query<Permission>(
    `UPDATE admin_permission SET name = $2, code = $3, description = $4 WHERE id = $1
     RETURNING id, name, code, description, created_at`,
    [id, name, code, description || null]
  );
  return result.rows[0] || null;
}

export async function deletePermission(id: number): Promise<boolean> {
  const { query } = await import("./db");
  const { rowCount } = await query(
    "DELETE FROM admin_permission WHERE id = $1",
    [id]
  );
  return (rowCount ?? 0) > 0;
}

// ============ Role CRUD ============

export async function listRoles(): Promise<Role[]> {
  const { query } = await import("./db");
  const result = await query<Role>(
    "SELECT id, name, description, created_at FROM admin_role ORDER BY id"
  );
  return result.rows;
}

export async function getRoleById(id: number): Promise<Role | null> {
  const { query } = await import("./db");
  const result = await query<Role>(
    "SELECT id, name, description, created_at FROM admin_role WHERE id = $1",
    [id]
  );
  return result.rows[0] || null;
}

export async function createRole(
  name: string,
  description?: string
): Promise<Role> {
  const { query } = await import("./db");
  const result = await query<Role>(
    `INSERT INTO admin_role (name, description) VALUES ($1, $2)
     RETURNING id, name, description, created_at`,
    [name, description || null]
  );
  return result.rows[0];
}

export async function updateRole(
  id: number,
  name: string,
  description?: string
): Promise<Role | null> {
  const { query } = await import("./db");
  const result = await query<Role>(
    `UPDATE admin_role SET name = $2, description = $3 WHERE id = $1
     RETURNING id, name, description, created_at`,
    [id, name, description || null]
  );
  return result.rows[0] || null;
}

export async function deleteRole(id: number): Promise<boolean> {
  const { query } = await import("./db");
  const { rowCount } = await query("DELETE FROM admin_role WHERE id = $1", [
    id,
  ]);
  return (rowCount ?? 0) > 0;
}

// ============ Role-Permission 关联 ============

export async function getRolePermissions(
  roleId: number
): Promise<Permission[]> {
  const { query } = await import("./db");
  const result = await query<Permission>(
    `SELECT p.id, p.name, p.code, p.description, p.created_at
     FROM admin_permission p
     JOIN admin_role_permission rp ON rp.permission_id = p.id
     WHERE rp.role_id = $1
     ORDER BY p.id`,
    [roleId]
  );
  return result.rows;
}

export async function setRolePermissions(
  roleId: number,
  permissionIds: number[]
): Promise<void> {
  const { transaction } = await import("./db");
  await transaction(async (client) => {
    await client.query(
      "DELETE FROM admin_role_permission WHERE role_id = $1",
      [roleId]
    );
    for (const permId of permissionIds) {
      await client.query(
        "INSERT INTO admin_role_permission (role_id, permission_id) VALUES ($1, $2)",
        [roleId, permId]
      );
    }
  });
}

// ============ User CRUD ============

export async function listUsers(
  options: { page?: number; pageSize?: number; search?: string } = {}
): Promise<{ users: AdminUser[]; total: number }> {
  const { query } = await import("./db");
  const page = options.page ?? 1;
  const pageSize = options.pageSize ?? 20;
  const offset = (page - 1) * pageSize;

  const params: unknown[] = [
    ...(options.search ? [`%${options.search}%`] : []),
    pageSize,
    offset,
  ];
  const whereClause = options.search ? `WHERE username ILIKE $1` : "";

  const countParams = options.search ? [`%${options.search}%`] : [];
  const countResult = await query<{ count: string }>(
    `SELECT COUNT(*) FROM admin_user ${whereClause}`,
    countParams
  );
  const total = parseInt(countResult.rows[0].count, 10);

  const result = await query<AdminUser>(
    `SELECT id, username, password_hash, is_active, created_at, updated_at
     FROM admin_user ${whereClause}
     ORDER BY id LIMIT $1 OFFSET $2`,
    params
  );

  return { users: result.rows, total };
}

export async function getUserById(id: number): Promise<AdminUser | null> {
  const { query } = await import("./db");
  const result = await query<AdminUser>(
    `SELECT id, username, password_hash, is_active, created_at, updated_at
     FROM admin_user WHERE id = $1`,
    [id]
  );
  return result.rows[0] || null;
}

export async function getUserByUsername(
  username: string
): Promise<AdminUser | null> {
  const { query } = await import("./db");
  const result = await query<AdminUser>(
    `SELECT id, username, password_hash, is_active, created_at, updated_at
     FROM admin_user WHERE username = $1`,
    [username]
  );
  return result.rows[0] || null;
}

export async function createUser(
  username: string,
  passwordHash: string
): Promise<AdminUser> {
  const { query } = await import("./db");
  const result = await query<AdminUser>(
    `INSERT INTO admin_user (username, password_hash, is_active)
     VALUES ($1, $2, true)
     RETURNING id, username, password_hash, is_active, created_at, updated_at`,
    [username, passwordHash]
  );
  return result.rows[0];
}

export async function updateUser(
  id: number,
  data: {
    passwordHash?: string;
    isActive?: boolean;
  }
): Promise<AdminUser | null> {
  const { query } = await import("./db");
  const updates: string[] = [];
  const params: unknown[] = [];
  let paramIdx = 1;

  if (data.passwordHash !== undefined) {
    updates.push(`password_hash = $${paramIdx++}`);
    params.push(data.passwordHash);
  }
  if (data.isActive !== undefined) {
    updates.push(`is_active = $${paramIdx++}`);
    params.push(data.isActive);
  }

  if (updates.length === 0) return getUserById(id);

  params.push(id);
  const result = await query<AdminUser>(
    `UPDATE admin_user SET ${updates.join(", ")}, updated_at = NOW()
     WHERE id = $${paramIdx}
     RETURNING id, username, password_hash, is_active, created_at, updated_at`,
    params
  );
  return result.rows[0] || null;
}

export async function deleteUser(id: number): Promise<boolean> {
  const { query } = await import("./db");
  const { rowCount } = await query("DELETE FROM admin_user WHERE id = $1", [
    id,
  ]);
  return (rowCount ?? 0) > 0;
}

// ============ User-Role 关联 ============

export async function getUserRoles(userId: number): Promise<Role[]> {
  const { query } = await import("./db");
  const result = await query<Role>(
    `SELECT r.id, r.name, r.description, r.created_at
     FROM admin_role r
     JOIN admin_user_role ur ON ur.role_id = r.id
     WHERE ur.user_id = $1
     ORDER BY r.id`,
    [userId]
  );
  return result.rows;
}

export async function getUserPermissions(
  userId: number
): Promise<string[]> {
  const { query } = await import("./db");
  const result = await query<{ code: string }>(
    `SELECT DISTINCT p.code
     FROM admin_permission p
     JOIN admin_role_permission rp ON rp.permission_id = p.id
     JOIN admin_user_role ur ON ur.role_id = rp.role_id
     WHERE ur.user_id = $1`,
    [userId]
  );
  return result.rows.map((r) => r.code);
}

export async function setUserRoles(
  userId: number,
  roleIds: number[]
): Promise<void> {
  const { transaction } = await import("./db");
  await transaction(async (client) => {
    await client.query("DELETE FROM admin_user_role WHERE user_id = $1", [
      userId,
    ]);
    for (const roleId of roleIds) {
      await client.query(
        "INSERT INTO admin_user_role (user_id, role_id) VALUES ($1, $2)",
        [userId, roleId]
      );
    }
  });
}

// ============ 权限检查 ============

export async function getAdminSession(
  userId: number,
  username: string
): Promise<AdminSession> {
  const [roles, permissions] = await Promise.all([
    getUserRoles(userId),
    getUserPermissions(userId),
  ]);

  return {
    userId,
    username,
    roles: roles.map((r) => r.name),
    permissions,
  };
}

export function hasPermission(
  session: AdminSession | null,
  requiredPermission: string
): boolean {
  if (!session) return false;
  return session.permissions.includes(requiredPermission);
}

export function hasRole(
  session: AdminSession | null,
  requiredRole: string
): boolean {
  if (!session) return false;
  return session.roles.includes(requiredRole);
}
