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

<cfcontent type="application/json; charset=utf-8" reset="true">
<cfheader name="Cache-Control" value="no-store">

<!---
ResolveBusiness API
===================
Resolves a beacon's (UUID, Major) to a Business.
Used by customer apps to identify which business a beacon belongs to.

Request:
  POST /api/beacon-sharding/resolve_business.cfm
  { "UUID": "f7826da6-4fa2-4e98-8024-bc5b71e0893e", "Major": 42 }

  Or for batch resolution:
  { "Beacons": [
      { "UUID": "...", "Major": 42 },
      { "UUID": "...", "Major": 43 }
    ]
  }

Response (single):
  {
    "OK": true,
    "BusinessID": 123,
    "BusinessName": "Joe's Diner",
    "BrandColor": "#FF5722",
    "HeaderImageURL": "/uploads/businesses/123/header.jpg"
  }

Response (batch):
  {
    "OK": true,
    "Results": [
      { "UUID": "...", "Major": 42, "BusinessID": 123, "BusinessName": "..." },
      { "UUID": "...", "Major": 43, "BusinessID": null, "Error": "not_found" }
    ]
  }
--->

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

function readJsonBody() {
  raw = toString(getHttpRequestData().content);
  if (isNull(raw) || len(trim(raw)) EQ 0) return {};
  try {
    parsed = deserializeJSON(raw);
  } catch(any e) {
    apiAbort({ OK=false, ERROR="bad_json", MESSAGE="Invalid JSON body" });
  }
  if (!isStruct(parsed)) return {};
  return parsed;
}

function normStr(v) {
  if (isNull(v)) return "";
  return trim(toString(v));
}

function resolveSingleBusiness(uuid, major) {
  var qBiz = queryExecute(
    "SELECT b.ID, b.Name, b.BrandColor, b.HeaderImageExtension
     FROM Businesses b
     JOIN BeaconShards bs ON b.BeaconShardID = bs.ID
     WHERE bs.UUID = ? AND b.BeaconMajor = ?
     LIMIT 1",
    [
      { value=uuid, cfsqltype="cf_sql_varchar" },
      { value=major, cfsqltype="cf_sql_smallint" }
    ],
    { datasource="payfrit" }
  );

  if (qBiz.recordCount EQ 0) {
    return { Found=false, Error="not_found" };
  }

  var headerImageURL = "";
  if (len(qBiz.HeaderImageExtension)) {
    headerImageURL = "/uploads/businesses/#qBiz.ID#/header.#qBiz.HeaderImageExtension#";
  }

  return {
    Found = true,
    BusinessID = qBiz.ID,
    BusinessName = qBiz.Name,
    BrandColor = qBiz.BrandColor,
    HeaderImageURL = headerImageURL
  };
}

data = readJsonBody();

// Check for batch request
if (structKeyExists(data, "Beacons") && isArray(data.Beacons)) {
  results = [];
  for (beacon in data.Beacons) {
    uuid = normStr(structKeyExists(beacon, "UUID") ? beacon.UUID : "");
    major = structKeyExists(beacon, "Major") && isNumeric(beacon.Major) ? int(beacon.Major) : 0;

    if (len(uuid) EQ 0 || major LTE 0) {
      arrayAppend(results, { UUID=uuid, Major=major, BusinessID=javaCast("null",""), Error="invalid_params" });
      continue;
    }

    resolved = resolveSingleBusiness(uuid, major);
    if (resolved.Found) {
      arrayAppend(results, {
        UUID = uuid,
        Major = major,
        BusinessID = resolved.BusinessID,
        BusinessName = resolved.BusinessName,
        BrandColor = resolved.BrandColor,
        HeaderImageURL = resolved.HeaderImageURL
      });
    } else {
      arrayAppend(results, { UUID=uuid, Major=major, BusinessID=javaCast("null",""), Error="not_found" });
    }
  }

  writeOutput(serializeJSON({ OK=true, COUNT=arrayLen(results), Results=results }));
  abort;
}

// Single request
uuid = normStr(structKeyExists(data, "UUID") ? data.UUID : "");
major = 0;
if (structKeyExists(data, "Major") && isNumeric(data.Major)) {
  major = int(data.Major);
}

// Also check URL params
if (len(uuid) EQ 0 && structKeyExists(url, "UUID")) {
  uuid = normStr(url.UUID);
}
if (major LTE 0 && structKeyExists(url, "Major") && isNumeric(url.Major)) {
  major = int(url.Major);
}

if (len(uuid) EQ 0) {
  apiAbort({ OK=false, ERROR="missing_uuid", MESSAGE="UUID is required" });
}
if (major LTE 0) {
  apiAbort({ OK=false, ERROR="missing_major", MESSAGE="Major is required" });
}

resolved = resolveSingleBusiness(uuid, major);

if (!resolved.Found) {
  apiAbort({ OK=false, ERROR="not_found", MESSAGE="No business found for this beacon" });
}
</cfscript>

<cfoutput>#serializeJSON({
  OK = true,
  BusinessID = resolved.BusinessID,
  BusinessName = resolved.BusinessName,
  BrandColor = resolved.BrandColor,
  HeaderImageURL = resolved.HeaderImageURL
})#</cfoutput>

<cfcatch type="any">
  <cfheader statuscode="200" statustext="OK">
  <cfcontent type="application/json; charset=utf-8" reset="true">
  <cfoutput>#serializeJSON({ OK=false, ERROR="server_error", MESSAGE=cfcatch.message, DETAIL=cfcatch.detail })#</cfoutput>
</cfcatch>
</cftry>
