<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">

<!---
  Payfrit API Application.cfm

  FIX: Provide a DEFAULT datasource so endpoints can call queryExecute()
  without specifying { datasource="payfrit" } every time.

  Token-auth gate for /api endpoints.

  Public allowlist (NO auth):
    - /api/login.cfm
    - /api/logout.cfm
    - /api/auth/login.cfm
    - /api/auth/logout.cfm
    - /api/businesses/list.cfm
    - /api/servicepoints/list.cfm

  Authenticated requests should send:
    - Header: X-User-Token: <token from /api/auth/login.cfm>
    - Header: X-Business-ID: <selected BusinessID> (required for most endpoints)
--->

<!--- OPTION A: default datasource for the whole app (THIS fixes "missing datasource") --->
<cfapplication
  name="payfrit_api"
  sessionmanagement="true"
  clientmanagement="false"
  setclientcookies="false"
  datasource="payfrit"
>

<cfscript>
function apiAbort(payload) {
  writeOutput(serializeJSON(payload));
  abort;
}

function headerValue(name) {
  k = "HTTP_" & ucase(reReplace(arguments.name, "[^A-Za-z0-9]", "_", "all"));
  if (structKeyExists(cgi, k)) return trim(cgi[k]);
  return "";
}

// Determine request path
request._api_scriptName = "";
if (structKeyExists(cgi, "SCRIPT_NAME")) {
  request._api_scriptName = cgi.SCRIPT_NAME;
} else if (structKeyExists(cgi, "PATH_INFO")) {
  request._api_scriptName = cgi.PATH_INFO;
}
request._api_path = lcase(request._api_scriptName);

// Public allowlist
request._api_isPublic = false;
if (len(request._api_path)) {
  if (findNoCase("/api/login.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/logout.cfm", request._api_path)) request._api_isPublic = true;

  if (findNoCase("/api/auth/login.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/auth/logout.cfm", request._api_path)) request._api_isPublic = true;

  if (findNoCase("/api/businesses/list.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/servicepoints/list.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/menu/items.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/orders/getOrCreateCart.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/orders/getCart.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/orders/setLineItem.cfm", request._api_path)) request._api_isPublic = true;
  if (findNoCase("/api/orders/submit.cfm", request._api_path)) request._api_isPublic = true;
}

// Carry session values into request (if present)
if (!structKeyExists(request, "UserID") && structKeyExists(session, "UserID")) {
  request.UserID = Duplicate(session.UserID);
}
if (!structKeyExists(request, "BusinessID") && structKeyExists(session, "BusinessID")) {
  request.BusinessID = Duplicate(session.BusinessID);
}

// Token auth: X-User-Token -> request.UserID
request._api_userToken = headerValue("X-User-Token");
if (len(request._api_userToken)) {
  try {
    request._api_qTok = queryExecute(
      "SELECT UserID FROM UserTokens WHERE Token = ? LIMIT 1",
      [ { value = request._api_userToken, cfsqltype = "cf_sql_varchar" } ],
      { datasource = "payfrit" }
    );

    if (request._api_qTok.recordCount EQ 1) {
      request.UserID = request._api_qTok.UserID;
      session.UserID = request._api_qTok.UserID;
    }
  } catch (any e) {
    // ignore; treated as unauthenticated
  }
}

// Business header: X-Business-ID -> request.BusinessID
request._api_hdrBiz = headerValue("X-Business-ID");
if (len(request._api_hdrBiz) && isNumeric(request._api_hdrBiz)) {
  request.BusinessID = int(request._api_hdrBiz);
  session.BusinessID = request.BusinessID;
}

// Enforce auth (except public)
if (!request._api_isPublic) {
  if (!structKeyExists(request, "UserID") || !isNumeric(request.UserID) || request.UserID LTE 0) {
    apiAbort({ "OK": false, "ERROR": "not_logged_in" });
  }
  if (!structKeyExists(request, "BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0) {
    apiAbort({ "OK": false, "ERROR": "no_business_selected" });
  }
}
</cfscript>
