import { scryptSync, timingSafeEqual } from "node:crypto";
import { getPool } from "../db/pool";

export type RoleEntity = {
  id: string;
  name: string;
  permissions_json: unknown;
  created_at: string;
};

export type UserEntity = {
  id: string;
  name: string;
  email: string;
  password_hash: string;
  role_id: string;
  status: "active" | "inactive";
  created_at: string;
  role_name: string;
  permissions_json: unknown;
};

function mapUser(row: any): UserEntity {
  return {
    id: row.id,
    name: row.name,
    email: row.email,
    password_hash: row.password_hash,
    role_id: row.role_id,
    status: row.status,
    created_at: row.created_at,
    role_name: row.role_name,
    permissions_json: row.permissions_json
  };
}

export async function getUserByEmail(email: string): Promise<UserEntity | null> {
  const { rows } = await getPool().query(
    `SELECT users.*, roles.name AS role_name, roles.permissions_json
       FROM users
       JOIN roles ON roles.id = users.role_id
      WHERE LOWER(users.email) = LOWER($1)
      LIMIT 1`,
    [email]
  );
  return rows[0] ? mapUser(rows[0]) : null;
}

function verifyScryptPassword(stored: string, password: string) {
  const parts = stored.split("$");
  if (parts.length !== 4 || parts[0] !== "scrypt") {
    return false;
  }
  const [, salt, keyLengthRaw, expectedHex] = parts;
  const keyLength = Number(keyLengthRaw);
  if (!salt || !Number.isFinite(keyLength) || keyLength <= 0 || !expectedHex) {
    return false;
  }
  const expected = Buffer.from(expectedHex, "hex");
  const actual = scryptSync(password, salt, keyLength);
  return expected.length === actual.length && timingSafeEqual(expected, actual);
}

export function verifyPassword(stored: string, password: string) {
  if (stored.startsWith("scrypt$")) {
    return verifyScryptPassword(stored, password);
  }

  return stored === password;
}

export function hasPermission(permissions: unknown, permission: string) {
  if (!permission) {
    return true;
  }
  if (Array.isArray(permissions)) {
    return permissions.includes("*") || permissions.includes(permission);
  }
  if (!permissions || typeof permissions !== "object") {
    return false;
  }

  const adminValue = (permissions as Record<string, unknown>).admin;
  if (adminValue === "*" || adminValue === true) {
    return true;
  }

  const value = (permissions as Record<string, unknown>)[permission];
  if (value === true || value === "*") {
    return true;
  }

  const [domain, action] = permission.split(":");
  const domainValue = (permissions as Record<string, unknown>)[domain];
  if (domainValue === "*" || domainValue === true) {
    return true;
  }
  if (Array.isArray(domainValue)) {
    return domainValue.includes("*") || domainValue.includes(action);
  }
  if (domainValue && typeof domainValue === "object") {
    const scoped = domainValue as Record<string, unknown>;
    return scoped[action] === true || scoped[action] === "*";
  }

  return false;
}
