<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 = {}; }

ownerBusinessID = val(data.OwnerBusinessID ?: 0);
guestBusinessID = val(data.GuestBusinessID ?: 0);
servicePointID = val(data.ServicePointID ?: 0);
economicsType = trim(data.EconomicsType ?: "none");
economicsValue = val(data.EconomicsValue ?: 0);
eligibilityScope = trim(data.EligibilityScope ?: "public");
timePolicyType = trim(data.TimePolicyType ?: "always");
timePolicyData = structKeyExists(data, "TimePolicyData") ? data.TimePolicyData : "";

// Validate required fields
if (ownerBusinessID LTE 0 || guestBusinessID LTE 0 || servicePointID LTE 0) {
    apiAbort({ "OK": false, "ERROR": "missing_params", "MESSAGE": "OwnerBusinessID, GuestBusinessID, and ServicePointID are required." });
}

if (ownerBusinessID == guestBusinessID) {
    apiAbort({ "OK": false, "ERROR": "self_grant", "MESSAGE": "Cannot grant access to your own business." });
}

// Validate caller is the owner of OwnerBusinessID
callerUserID = val(structKeyExists(request, "UserID") ? request.UserID : 0);
if (callerUserID LTE 0) {
    apiAbort({ "OK": false, "ERROR": "not_authenticated", "MESSAGE": "Authentication required." });
}

qOwner = queryTimed(
    "SELECT UserID FROM Businesses WHERE ID = ? LIMIT 1",
    [{ value = ownerBusinessID, cfsqltype = "cf_sql_integer" }],
    { datasource = "payfrit" }
);
if (qOwner.recordCount == 0 || qOwner.UserID != callerUserID) {
    apiAbort({ "OK": false, "ERROR": "not_owner", "MESSAGE": "You are not the owner of this business." });
}

// Validate ServicePoint belongs to OwnerBusinessID
qSP = queryTimed(
    "SELECT ID FROM ServicePoints WHERE ID = ? AND BusinessID = ? LIMIT 1",
    [
        { value = servicePointID, cfsqltype = "cf_sql_integer" },
        { value = ownerBusinessID, cfsqltype = "cf_sql_integer" }
    ],
    { datasource = "payfrit" }
);
if (qSP.recordCount == 0) {
    apiAbort({ "OK": false, "ERROR": "sp_not_owned", "MESSAGE": "Service point does not belong to your business." });
}

// Validate GuestBusiness exists
qGuest = queryTimed(
    "SELECT ID FROM Businesses WHERE ID = ? LIMIT 1",
    [{ value = guestBusinessID, cfsqltype = "cf_sql_integer" }],
    { datasource = "payfrit" }
);
if (qGuest.recordCount == 0) {
    apiAbort({ "OK": false, "ERROR": "guest_not_found", "MESSAGE": "Guest business not found." });
}

// Check no active or pending grant exists for this combo
qExisting = queryTimed(
    "SELECT ID FROM ServicePointGrants
     WHERE OwnerBusinessID = ? AND GuestBusinessID = ? AND ServicePointID = ? AND StatusID IN (0, 1)
     LIMIT 1",
    [
        { value = ownerBusinessID, cfsqltype = "cf_sql_integer" },
        { value = guestBusinessID, cfsqltype = "cf_sql_integer" },
        { value = servicePointID, cfsqltype = "cf_sql_integer" }
    ],
    { datasource = "payfrit" }
);
if (qExisting.recordCount > 0) {
    apiAbort({ "OK": false, "ERROR": "grant_exists", "MESSAGE": "An active or pending grant already exists for this service point and guest business." });
}

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

if (!arrayFind(validEconomics, economicsType)) economicsType = "none";
if (!arrayFind(validEligibility, eligibilityScope)) eligibilityScope = "public";
if (!arrayFind(validTimePolicy, timePolicyType)) timePolicyType = "always";

// Generate UUID and InviteToken
newUUID = createObject("java", "java.util.UUID").randomUUID().toString();
inviteToken = lcase(hash(generateSecretKey("AES", 256), "SHA-256"));

// Serialize TimePolicyData
timePolicyJson = javaCast("null", "");
if (isStruct(timePolicyData) && !structIsEmpty(timePolicyData)) {
    timePolicyJson = serializeJSON(timePolicyData);
} else if (isSimpleValue(timePolicyData) && len(trim(timePolicyData))) {
    timePolicyJson = timePolicyData;
}

// Insert grant
queryTimed(
    "INSERT INTO ServicePointGrants
        (UUID, OwnerBusinessID, GuestBusinessID, ServicePointID, StatusID,
         EconomicsType, EconomicsValue, EligibilityScope, TimePolicyType, TimePolicyData,
         InviteToken, CreatedByUserID)
     VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?)",
    [
        { value = newUUID, cfsqltype = "cf_sql_varchar" },
        { value = ownerBusinessID, cfsqltype = "cf_sql_integer" },
        { value = guestBusinessID, cfsqltype = "cf_sql_integer" },
        { value = servicePointID, cfsqltype = "cf_sql_integer" },
        { value = economicsType, cfsqltype = "cf_sql_varchar" },
        { value = economicsValue, cfsqltype = "cf_sql_decimal" },
        { value = eligibilityScope, cfsqltype = "cf_sql_varchar" },
        { value = timePolicyType, cfsqltype = "cf_sql_varchar" },
        { value = timePolicyJson, cfsqltype = "cf_sql_varchar", null = isNull(timePolicyJson) },
        { value = inviteToken, cfsqltype = "cf_sql_varchar" },
        { value = callerUserID, cfsqltype = "cf_sql_integer" }
    ],
    { datasource = "payfrit" }
);

// Get the inserted grant ID
qNew = queryTimed(
    "SELECT ID FROM ServicePointGrants WHERE UUID = ? LIMIT 1",
    [{ value = newUUID, cfsqltype = "cf_sql_varchar" }],
    { datasource = "payfrit" }
);
grantID = qNew.ID;

// Record history
recordGrantHistory(
    grantID = grantID,
    action = "created",
    actorUserID = callerUserID,
    actorBusinessID = ownerBusinessID,
    newData = {
        "OwnerBusinessID": ownerBusinessID,
        "GuestBusinessID": guestBusinessID,
        "ServicePointID": servicePointID,
        "EconomicsType": economicsType,
        "EconomicsValue": economicsValue,
        "EligibilityScope": eligibilityScope,
        "TimePolicyType": timePolicyType
    }
);

writeOutput(serializeJSON({
    "OK": true,
    "GrantID": grantID,
    "UUID": newUUID,
    "InviteToken": inviteToken,
    "StatusID": 0,
    "MESSAGE": "Grant created. Awaiting guest acceptance."
}));
</cfscript>
