import { createHmac, timingSafeEqual } from "crypto";
import { cookies } from "next/headers";

export const ADMIN_SESSION_COOKIE = "moh_admin_session";
const ADMIN_FAIL_COOKIE = "moh_admin_fail";
const ADMIN_SESSION_VALUE = "superadmin";
const MAX_FAILED_ATTEMPTS = 5;
const LOCKOUT_SECONDS = 15 * 60;

function getSecret(): string {
  return process.env.ADMIN_AUTH_SECRET ?? "";
}

function getPassword(): string {
  return process.env.ADMIN_PASSWORD ?? "";
}

function signValue(value: string): string {
  return createHmac("sha256", getSecret()).update(value).digest("hex");
}

function buildToken(): string {
  return `${ADMIN_SESSION_VALUE}.${signValue(ADMIN_SESSION_VALUE)}`;
}

function verifyToken(token: string): boolean {
  const parts = token.split(".");
  if (parts.length !== 2) {
    return false;
  }

  const [value, signature] = parts;
  if (value !== ADMIN_SESSION_VALUE) {
    return false;
  }

  const expected = signValue(value);
  const left = Buffer.from(signature);
  const right = Buffer.from(expected);

  if (left.length !== right.length) {
    return false;
  }

  return timingSafeEqual(left, right);
}

export function isAdminAuthConfigured(): boolean {
  return getPassword().length > 0 && getSecret().length > 0;
}

export function isPasswordValid(password: string): boolean {
  if (!isAdminAuthConfigured()) {
    return false;
  }

  const provided = Buffer.from(password);
  const expected = Buffer.from(getPassword());

  if (provided.length !== expected.length) {
    return false;
  }

  return timingSafeEqual(provided, expected);
}

export function setAdminSessionCookie(): void {
  const store = cookies();
  store.set(ADMIN_SESSION_COOKIE, buildToken(), {
    httpOnly: true,
    sameSite: "lax",
    secure: process.env.NODE_ENV === "production",
    path: "/",
    maxAge: 60 * 60 * 8,
  });
}

export function clearAdminSessionCookie(): void {
  const store = cookies();
  store.set(ADMIN_SESSION_COOKIE, "", {
    httpOnly: true,
    sameSite: "lax",
    secure: process.env.NODE_ENV === "production",
    path: "/",
    maxAge: 0,
  });
}

type FailState = {
  attempts: number;
  lockUntil: number;
};

function parseFailState(rawValue: string | undefined): FailState {
  if (!rawValue) {
    return { attempts: 0, lockUntil: 0 };
  }

  try {
    const parsed = JSON.parse(rawValue) as Partial<FailState>;

    return {
      attempts: Number(parsed.attempts ?? 0),
      lockUntil: Number(parsed.lockUntil ?? 0),
    };
  } catch {
    return { attempts: 0, lockUntil: 0 };
  }
}

export function getAdminLockState(): { locked: boolean; remainingSeconds: number } {
  const store = cookies();
  const state = parseFailState(store.get(ADMIN_FAIL_COOKIE)?.value);
  const now = Date.now();

  if (state.lockUntil > now) {
    return {
      locked: true,
      remainingSeconds: Math.ceil((state.lockUntil - now) / 1000),
    };
  }

  return { locked: false, remainingSeconds: 0 };
}

export function registerFailedAdminAttempt(): { locked: boolean; remainingSeconds: number } {
  const store = cookies();
  const now = Date.now();
  const current = parseFailState(store.get(ADMIN_FAIL_COOKIE)?.value);
  const attempts = current.lockUntil > now ? current.attempts : current.attempts + 1;
  const locked = attempts >= MAX_FAILED_ATTEMPTS;
  const lockUntil = locked ? now + LOCKOUT_SECONDS * 1000 : 0;

  store.set(ADMIN_FAIL_COOKIE, JSON.stringify({ attempts, lockUntil }), {
    httpOnly: true,
    sameSite: "lax",
    secure: process.env.NODE_ENV === "production",
    path: "/",
    maxAge: LOCKOUT_SECONDS,
  });

  return {
    locked,
    remainingSeconds: locked ? LOCKOUT_SECONDS : 0,
  };
}

export function resetAdminFailedAttempts(): void {
  const store = cookies();
  store.set(ADMIN_FAIL_COOKIE, "", {
    httpOnly: true,
    sameSite: "lax",
    secure: process.env.NODE_ENV === "production",
    path: "/",
    maxAge: 0,
  });
}

export function isAdminAuthenticated(): boolean {
  if (!isAdminAuthConfigured()) {
    return false;
  }

  const store = cookies();
  const token = store.get(ADMIN_SESSION_COOKIE)?.value;

  if (!token) {
    return false;
  }

  return verifyToken(token);
}
