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

<cfinclude template="_grantUtils.cfm">

<cfscript>
data = {};
try {
    raw = toString(getHttpRequestData().content);
    if (len(trim(raw))) {
        data = deserializeJSON(raw);
        if (!isStruct(data)) data = {};
    }
} catch (any e) { data = {}; }

grantID = val(data.GrantID ?: 0);
if (grantID LTE 0) {
    apiAbort({ "OK": false, "ERROR": "missing_grantid", "MESSAGE": "GrantID is required." });
}

callerUserID = val(structKeyExists(request, "UserID") ? request.UserID : 0);
if (callerUserID LTE 0) {
    apiAbort({ "OK": false, "ERROR": "not_authenticated" });
}

// Load current grant
qGrant = queryTimed(
    "SELECT g.*, b.UserID AS OwnerUserID
     FROM ServicePointGrants g
     JOIN Businesses b ON b.ID = g.OwnerBusinessID
     WHERE g.ID = ?
     LIMIT 1",
    [{ value = grantID, cfsqltype = "cf_sql_integer" }],
    { datasource = "payfrit" }
);

if (qGrant.recordCount == 0) {
    apiAbort({ "OK": false, "ERROR": "not_found", "MESSAGE": "Grant not found." });
}

if (qGrant.OwnerUserID != callerUserID) {
    apiAbort({ "OK": false, "ERROR": "not_owner", "MESSAGE": "Only the owner business can update grant terms." });
}

if (qGrant.StatusID != 0 && qGrant.StatusID != 1) {
    apiAbort({ "OK": false, "ERROR": "bad_state", "MESSAGE": "Only pending or active grants can be updated." });
}

// Collect updates
setClauses = [];
setParams = [];
previousData = {};
newData = {};

validEconomics = ["none", "flat_fee", "percent_of_orders"];
validEligibility = ["public", "employees", "guests", "internal"];
validTimePolicy = ["always", "schedule", "date_range", "event"];

// Economics
if (structKeyExists(data, "EconomicsType") || structKeyExists(data, "EconomicsValue")) {
    eType = trim(data.EconomicsType ?: qGrant.EconomicsType);
    eValue = val(structKeyExists(data, "EconomicsValue") ? data.EconomicsValue : qGrant.EconomicsValue);
    if (!arrayFind(validEconomics, eType)) eType = qGrant.EconomicsType;

    if (eType != qGrant.EconomicsType || eValue != qGrant.EconomicsValue) {
        previousData["EconomicsType"] = qGrant.EconomicsType;
        previousData["EconomicsValue"] = qGrant.EconomicsValue;
        newData["EconomicsType"] = eType;
        newData["EconomicsValue"] = eValue;
        arrayAppend(setClauses, "EconomicsType = ?");
        arrayAppend(setParams, { value = eType, cfsqltype = "cf_sql_varchar" });
        arrayAppend(setClauses, "EconomicsValue = ?");
        arrayAppend(setParams, { value = eValue, cfsqltype = "cf_sql_decimal" });
    }
}

// Eligibility
if (structKeyExists(data, "EligibilityScope")) {
    eScope = trim(data.EligibilityScope);
    if (!arrayFind(validEligibility, eScope)) eScope = qGrant.EligibilityScope;
    if (eScope != qGrant.EligibilityScope) {
        previousData["EligibilityScope"] = qGrant.EligibilityScope;
        newData["EligibilityScope"] = eScope;
        arrayAppend(setClauses, "EligibilityScope = ?");
        arrayAppend(setParams, { value = eScope, cfsqltype = "cf_sql_varchar" });
    }
}

// Time policy
if (structKeyExists(data, "TimePolicyType") || structKeyExists(data, "TimePolicyData")) {
    tType = trim(data.TimePolicyType ?: qGrant.TimePolicyType);
    if (!arrayFind(validTimePolicy, tType)) tType = qGrant.TimePolicyType;
    tData = structKeyExists(data, "TimePolicyData") ? data.TimePolicyData : qGrant.TimePolicyData;

    changed = (tType != qGrant.TimePolicyType);
    if (!changed && isStruct(tData)) {
        changed = (serializeJSON(tData) != (qGrant.TimePolicyData ?: ""));
    }

    if (changed) {
        previousData["TimePolicyType"] = qGrant.TimePolicyType;
        previousData["TimePolicyData"] = qGrant.TimePolicyData ?: "";
        newData["TimePolicyType"] = tType;
        newData["TimePolicyData"] = tData;
        arrayAppend(setClauses, "TimePolicyType = ?");
        arrayAppend(setParams, { value = tType, cfsqltype = "cf_sql_varchar" });

        tDataJson = javaCast("null", "");
        if (isStruct(tData) && !structIsEmpty(tData)) {
            tDataJson = serializeJSON(tData);
        } else if (isSimpleValue(tData) && len(trim(tData))) {
            tDataJson = tData;
        }
        arrayAppend(setClauses, "TimePolicyData = ?");
        arrayAppend(setParams, { value = tDataJson, cfsqltype = "cf_sql_varchar", null = isNull(tDataJson) });
    }
}

if (arrayLen(setClauses) == 0) {
    apiAbort({ "OK": true, "MESSAGE": "No changes detected.", "GrantID": grantID });
}

// Execute update
arrayAppend(setParams, { value = grantID, cfsqltype = "cf_sql_integer" });
queryTimed(
    "UPDATE ServicePointGrants SET #arrayToList(setClauses, ', ')# WHERE ID = ?",
    setParams,
    { datasource = "payfrit" }
);

// Determine action name for history
action = "updated";
if (structKeyExists(newData, "EconomicsType") || structKeyExists(newData, "EconomicsValue")) action = "updated_economics";
if (structKeyExists(newData, "EligibilityScope")) action = "updated_eligibility";
if (structKeyExists(newData, "TimePolicyType")) action = "updated_time_policy";

recordGrantHistory(
    grantID = grantID,
    action = action,
    actorUserID = callerUserID,
    actorBusinessID = qGrant.OwnerBusinessID,
    previousData = previousData,
    newData = newData
);

writeOutput(serializeJSON({
    "OK": true,
    "GrantID": grantID,
    "MESSAGE": "Grant updated."
}));
</cfscript>
