import type { CookieOptions, Request, Response } from 'express';
import config from '@/shared/config';

interface SessionTokens {
  accessToken: string;
  refreshToken: string;
}

function encodeCookieValue(value: string): string {
  return encodeURIComponent(value);
}

function decodeCookieValue(value: string): string {
  try {
    return decodeURIComponent(value);
  } catch {
    return value;
  }
}

function getBaseCookieOptions(maxAge: number): CookieOptions {
  const options: CookieOptions = {
    httpOnly: true,
    maxAge,
    path: config.auth.cookiePath,
    sameSite: config.auth.cookieSameSite,
    secure: config.auth.cookieSecure,
  };

  if (config.auth.cookieDomain) {
    options.domain = config.auth.cookieDomain;
  }

  return options;
}

function getClearCookieOptions(): CookieOptions {
  const options: CookieOptions = {
    path: config.auth.cookiePath,
  };

  if (config.auth.cookieDomain) {
    options.domain = config.auth.cookieDomain;
  }

  return options;
}

function getAccessCookieOptions(): CookieOptions {
  return getBaseCookieOptions(config.auth.accessTokenMaxAgeMs);
}

function getRefreshCookieOptions(): CookieOptions {
  return getBaseCookieOptions(config.auth.refreshTokenMaxAgeMs);
}

function setAccessCookie(res: Response, token: string): void {
  res.cookie(config.auth.accessCookieName, token, getAccessCookieOptions());
}

function setRefreshCookie(res: Response, token: string): void {
  res.cookie(config.auth.refreshCookieName, token, getRefreshCookieOptions());
}

function setSessionCookies(res: Response, session: SessionTokens): void {
  setAccessCookie(res, session.accessToken);
  setRefreshCookie(res, session.refreshToken);
}

function clearAccessCookie(res: Response): void {
  res.clearCookie(config.auth.accessCookieName, getClearCookieOptions());
}

function clearRefreshCookie(res: Response): void {
  res.clearCookie(config.auth.refreshCookieName, getClearCookieOptions());
}

function clearSessionCookies(res: Response): void {
  clearAccessCookie(res);
  clearRefreshCookie(res);
}

function extractCookie(req: Request, cookieName: string): string | null {
  const cookieHeader = req.headers.cookie;

  if (!cookieHeader) {
    return null;
  }

  const cookies = cookieHeader.split(';');

  for (const cookie of cookies) {
    const separatorIndex = cookie.indexOf('=');

    if (separatorIndex === -1) {
      continue;
    }

    const name = cookie.slice(0, separatorIndex).trim();

    if (name !== cookieName) {
      continue;
    }

    const value = cookie.slice(separatorIndex + 1).trim();
    return decodeCookieValue(value);
  }

  return null;
}

function extractAccessCookie(req: Request): string | null {
  return extractCookie(req, config.auth.accessCookieName);
}

function extractRefreshCookie(req: Request): string | null {
  return extractCookie(req, config.auth.refreshCookieName);
}

function serializeAccessCookie(token: string): string {
  return `${config.auth.accessCookieName}=${encodeCookieValue(token)}`;
}

export default {
  clearAccessCookie,
  clearRefreshCookie,
  clearSessionCookies,
  extractAccessCookie,
  extractRefreshCookie,
  getAccessCookieOptions,
  getRefreshCookieOptions,
  serializeAccessCookie,
  setAccessCookie,
  setRefreshCookie,
  setSessionCookies,
};
