<?php
//	After application initialized event
// ======================================================
// DSWD UNIVERSAL TRANSACTION HISTORY ENGINE V2
//
// LOCATION:
// Global Events -> After App Init
//
// IMPORTANT:
// DELETE THE OLD UNIVERSAL HISTORY FUNCTIONS FIRST.
// DO NOT APPEND THIS BELOW THE OLD VERSION.
//
// SUPPORTS:
// - Add Page
// - Inline Add
// - Popup Add
// - Edit Page
// - Inline Edit
// - Popup Edit
// - Delete
// - Multiple status fields
// - Approval
// - Reject
// - Return
// - Cancel
// - Logistics statuses
// - Manual workflow buttons
// - Activity Log Preview
// - View All
// - Duplicate protection
// ======================================================



// ======================================================
// DEBUG MODE
//
// FALSE = normal production use
// TRUE  = write history diagnostics to PHP error_log
// ======================================================

$GLOBALS["DSWD_HISTORY_DEBUG"] = false;



// ======================================================
// MODULE CONFIGURATION
//
// ADD FUTURE MODULES HERE ONLY.
// ======================================================

function dswdHistoryModules()
{
    return array(


        // ==================================================
        // LOGISTICS DRIVER APP ACCESS
        // ==================================================

        "LOGISTICS_DRIVER_APP_ACCESS" => array(

            "table" =>
                "logistics_driver_app_access",

            "pk" =>
                "id",

            "record_no" =>
                "access_no",

            "label" =>
                "Logistics Driver App Access",

            "status_fields" => array(

                "access_status" =>
                    "Access Status"
            )
        ),


        // ==================================================
        // LOGISTICS PROVIDER
        // ==================================================

        "LOGISTICS_PROVIDER" => array(

            "table" =>
                "logistics_provider",

            "pk" =>
                "id",

            "record_no" =>
                "provider_code",

            "label" =>
                "Logistics Provider",

            "status_fields" => array(

                "status" =>
                    "Status"
            )
        ),


        // ==================================================
        // LOGISTICS VEHICLE
        // ==================================================

        "LOGISTICS_VEHICLE" => array(

            "table" =>
                "logistics_vehicle",

            "pk" =>
                "id",

            "record_no" =>
                "vehicle_code",

            "label" =>
                "Logistics Vehicle",

            "status_fields" => array(

                "status" =>
                    "Status"
            )
        ),


        // ==================================================
        // VEHICLE TYPE
        // ==================================================

        "VEHICLE_TYPE" => array(

            "table" =>
                "vehicle_type",

            "pk" =>
                "id",

            "record_no" =>
                "code",

            "label" =>
                "Vehicle Type",

            "status_fields" => array(

                "is_active" =>
                    "Active Status"
            )
        ),


        // ==================================================
        // SAVED LOCATION
        // ==================================================

        "SAVED_LOCATION" => array(

            "table" =>
                "saved_location",

            "pk" =>
                "id",

            "record_no" =>
                "location_code",

            "label" =>
                "Saved Location",

            "status_fields" => array(

                "active_flag" =>
                    "Active Status"
            )
        ),


        // ==================================================
        // LOCATION TYPE
        // ==================================================

        "LOCATION_TYPE" => array(

            "table" =>
                "location_type",

            "pk" =>
                "id",

            "record_no" =>
                "location_type_code",

            "label" =>
                "Location Type",

            "status_fields" => array(

                "active_flag" =>
                    "Active Status"
            )
        ),


        // ==================================================
        // DISPATCH PLAN
        // ==================================================

        "DISPATCH_PLAN" => array(

            "table" =>
                "dispatch_plan",

            "pk" =>
                "id",

            "record_no" =>
                "dispatch_plan_no",

            "label" =>
                "Dispatch Plan",

            "status_fields" => array(

                "dispatch_status" =>
                    "Dispatch Status"
            )
        ),


        // ==================================================
        // DELIVERY RECEIPT
        // ==================================================

        "DELIVERY_RECEIPT" => array(

            "table" =>
                "delivery_receipt",

            "pk" =>
                "id",

            "record_no" =>
                "delivery_receipt_no",

            "label" =>
                "Delivery Receipt",

            "status_fields" => array(

                "dispatch_status" =>
                    "Dispatch Status"
            )
        ),


        // ==================================================
        // TRIP TICKET
        //
        // Has several independent statuses.
        // ==================================================

        "TRIP_TICKET" => array(

            "table" =>
                "trip_ticket",

            "pk" =>
                "id",

            "record_no" =>
                "trip_ticket_no",

            "label" =>
                "Trip Ticket",

            "status_fields" => array(

                "trip_status" =>
                    "Trip Status",

                "delivery_status" =>
                    "Delivery Status",

                "app_trip_status" =>
                    "Driver App Status",

                "status" =>
                    "Status"
            )
        ),


        // ==================================================
        // LOGISTICS DELIVERY COMPLETION
        // ==================================================

        "LOGISTICS_DELIVERY_COMPLETION" => array(

            "table" =>
                "logistics_delivery_completion",

            "pk" =>
                "id",

            "record_no" =>
                "",

            "label" =>
                "Logistics Delivery Completion",

            "status_fields" => array(

                "completion_status" =>
                    "Completion Status"
            )
        ),


        // ==================================================
        // DIVERT
        //
        // Approval status + operational status.
        // ==================================================

        "DIVERT" => array(

            "table" =>
                "divert",

            "pk" =>
                "id",

            "record_no" =>
                "divert_no",

            "label" =>
                "Divert",

            "status_fields" => array(

                "approval_status" =>
                    "Approval Status",

                "status" =>
                    "Status"
            )
        ),


        // ==================================================
        // STF EXTERNAL
        // ==================================================

        "STF_EXTERNAL" => array(

            "table" =>
                "stock_transfer_form",

            "pk" =>
                "transfer_form_id",

            "record_no" =>
                "transfer_form_no",

            "label" =>
                "STF External",

            "status_fields" => array(

                "approval_status" =>
                    "Approval Status",

                "status" =>
                    "Transfer Status",

                "digital_signature_status" =>
                    "Digital Signature Status"
            )
        ),


        // ==================================================
        // STF INTERNAL
        // ==================================================

        "STF_INTERNAL" => array(

            "table" =>
                "stock_transfer_form",

            "pk" =>
                "transfer_form_id",

            "record_no" =>
                "transfer_form_no",

            "label" =>
                "STF Internal",

            "status_fields" => array(

                "approval_status" =>
                    "Approval Status",

                "status" =>
                    "Transfer Status",

                "digital_signature_status" =>
                    "Digital Signature Status"
            )
        ),


        // ==================================================
        // RIS LOGISTICS
        // ==================================================

        "RIS_LOGISTICS" => array(

            "table" =>
                "raw_material_request",

            "pk" =>
                "id",

            "record_no" =>
                "risp_no",

            "label" =>
                "RIS for Logistics",

            "status_fields" => array(

                "status" =>
                    "Status"
            )
        ),


        // ==================================================
        // RIS WAREHOUSE
        // ==================================================

        "RIS_WAREHOUSE" => array(

            "table" =>
                "raw_material_request",

            "pk" =>
                "id",

            "record_no" =>
                "risp_no",

            "label" =>
                "RIS for Warehouse",

            "status_fields" => array(

                "status" =>
                    "Status"
            )
        )
    );
}



// ======================================================
// DEBUG
// ======================================================

function dswdHistoryDebug($message)
{
    if (
        !empty(
            $GLOBALS[
                "DSWD_HISTORY_DEBUG"
            ]
        )
    ) {

        error_log(
            "[DSWD HISTORY] " .
            $message
        );
    }
}



// ======================================================
// GET MODULE CONFIGURATION
// ======================================================

function dswdHistoryConfig(
    $moduleCode
)
{
    $moduleCode =
        strtoupper(
            trim(
                (string)$moduleCode
            )
        );


    $modules =
        dswdHistoryModules();


    if (
        !isset(
            $modules[
                $moduleCode
            ]
        )
    ) {

        dswdHistoryDebug(
            "Unknown module: " .
            $moduleCode
        );

        return null;
    }


    $config =
        $modules[
            $moduleCode
        ];


    $config[
        "module_code"
    ] =
        $moduleCode;


    return $config;
}



// ======================================================
// ACTOR
// ======================================================

function dswdHistoryGetActor()
{
    $username =
        Security::getUserName();


    $userId =
        0;


    $actorName =
        trim(
            (string)$username
        );


    if ($username != "") {

        $safeUsername =
            db_addslashes(
                $username
            );


        $sqlUser = "
            SELECT
                ID,
                fullname

            FROM users

            WHERE username = '" .
                $safeUsername . "'

            LIMIT 1
        ";


        $rsUser =
            DB::Query(
                $sqlUser
            );


        $user =
            $rsUser
                ? $rsUser->fetchAssoc()
                : null;


        if ($user) {

            $userId =
                intval(
                    $user[
                        "ID"
                    ]
                );


            $fullName =
                trim(
                    (string)(
                        $user[
                            "fullname"
                        ]
                        ?? ""
                    )
                );


            if ($fullName != "") {

                $actorName =
                    $fullName;
            }
        }
    }


    if ($actorName == "") {

        $actorName =
            "System";
    }


    return array(

        "username" =>
            $username,

        "user_id" =>
            $userId,

        "actor_name" =>
            $actorName
    );
}



// ======================================================
// MANILA DATE/TIME
// ======================================================

function dswdHistoryNow()
{
    $dateNow =
        new DateTime(
            "now",
            new DateTimeZone(
                "Asia/Manila"
            )
        );


    return $dateNow->format(
        "Y-m-d H:i:s"
    );
}



// ======================================================
// GET RECORD ID
// ======================================================

function dswdHistoryGetRecordId(
    $config,
    $keys = array(),
    $values = array(),
    $oldvalues = array()
)
{
    $pk =
        $config["pk"];


    if (
        isset(
            $keys[
                $pk
            ]
        )
    ) {

        return intval(
            $keys[
                $pk
            ]
        );
    }


    if (
        isset(
            $values[
                $pk
            ]
        )
    ) {

        return intval(
            $values[
                $pk
            ]
        );
    }


    if (
        isset(
            $oldvalues[
                $pk
            ]
        )
    ) {

        return intval(
            $oldvalues[
                $pk
            ]
        );
    }


    // --------------------------------------------------
    // PHPRunner fallback:
    // If there is only one key, use that value.
    // --------------------------------------------------

    if (
        is_array(
            $keys
        ) &&
        count(
            $keys
        ) == 1
    ) {

        $firstKey =
            reset(
                $keys
            );


        if (
            is_numeric(
                $firstKey
            )
        ) {

            return intval(
                $firstKey
            );
        }
    }


    return 0;
}



// ======================================================
// GET RECORD NUMBER
// ======================================================

function dswdHistoryGetRecordNo(
    $config,
    $recordId,
    $values = array(),
    $oldvalues = array()
)
{
    $recordNoField =
        $config[
            "record_no"
        ];


    $recordNo =
        "";


    if ($recordNoField != "") {


        if (
            isset(
                $values[
                    $recordNoField
                ]
            )
        ) {

            $recordNo =
                trim(
                    (string)$values[
                        $recordNoField
                    ]
                );
        }


        if (
            $recordNo == "" &&
            isset(
                $oldvalues[
                    $recordNoField
                ]
            )
        ) {

            $recordNo =
                trim(
                    (string)$oldvalues[
                        $recordNoField
                    ]
                );
        }


        if ($recordNo == "") {

            $recordNo =
                trim(
                    (string)DBLookup(
                        "
                        SELECT `" .
                            $recordNoField .
                        "`

                        FROM `" .
                            $config[
                                "table"
                            ] .
                        "`

                        WHERE `" .
                            $config[
                                "pk"
                            ] .
                        "` = " .
                            intval(
                                $recordId
                            ) . "

                        LIMIT 1
                        "
                    )
                );
        }
    }


    if ($recordNo == "") {

        $recordNo =
            $config[
                "label"
            ] .
            " #" .
            intval(
                $recordId
            );
    }


    return $recordNo;
}



// ======================================================
// NORMALIZE STATUS FOR DISPLAY
// ======================================================

function dswdHistoryNormalizeStatus(
    $value,
    $fieldName = ""
)
{
    $value =
        trim(
            (string)$value
        );


    if (
        $fieldName ==
        "active_flag"
        ||
        $fieldName ==
        "is_active"
    ) {

        return intval(
            $value
        ) == 1

            ? "ACTIVE"

            : "INACTIVE";
    }


    return $value;
}



// ======================================================
// CANONICAL STATUS
//
// FOR APPROVAL
// FOR_APPROVAL
// For Approval
//
// become:
// FOR_APPROVAL
// ======================================================

function dswdHistoryCanonicalStatus(
    $status
)
{
    $status =
        strtoupper(
            trim(
                (string)$status
            )
        );


    $status =
        str_replace(
            array(
                "-",
                " "
            ),
            "_",
            $status
        );


    while (
        strpos(
            $status,
            "__"
        ) !== false
    ) {

        $status =
            str_replace(
                "__",
                "_",
                $status
            );
    }


    return $status;
}



// ======================================================
// STATUS -> HISTORY ACTION
// ======================================================

function dswdHistoryStatusAction(
    $status
)
{
    $status =
        dswdHistoryCanonicalStatus(
            $status
        );


    switch ($status) {


        // --------------------------------------------------
        // DRAFT / PENDING
        // --------------------------------------------------

        case "DRAFT":

            return array(
                "DRAFT",
                "Set to Draft by"
            );


        case "PENDING":

            return array(
                "PENDING",
                "Set to Pending by"
            );


        case "PENDING_FULFILLMENT":

            return array(
                "PENDING_FULFILLMENT",
                "Set to Pending Fulfillment by"
            );


        // --------------------------------------------------
        // APPROVAL
        // --------------------------------------------------

        case "FOR_APPROVAL":
        case "PENDING_APPROVAL":
        case "SUBMITTED":

            return array(
                "SUBMITTED",
                "Submitted by"
            );


        case "FOR_CHECKING":

            return array(
                "FOR_CHECKING",
                "Submitted for checking by"
            );


        case "CHECKED":

            return array(
                "CHECKED",
                "Checked by"
            );


        case "APPROVED":

            return array(
                "APPROVED",
                "Approved by"
            );


        case "REJECTED":
        case "DISAPPROVED":

            return array(
                "REJECTED",
                "Rejected by"
            );


        case "RETURNED":
        case "RETURN_FOR_REVISION":
        case "RETURNED_FOR_REVISION":

            return array(
                "RETURNED",
                "Returned by"
            );


        // --------------------------------------------------
        // ACTIVE STATUS
        // --------------------------------------------------

        case "ACTIVE":

            return array(
                "ACTIVATED",
                "Activated by"
            );


        case "INACTIVE":

            return array(
                "DEACTIVATED",
                "Deactivated by"
            );


        case "AVAILABLE":

            return array(
                "AVAILABLE",
                "Set available by"
            );


        case "IN_USE":

            return array(
                "IN_USE",
                "Set in use by"
            );


        case "UNDER_MAINTENANCE":

            return array(
                "UNDER_MAINTENANCE",
                "Set under maintenance by"
            );


        case "EXPIRED":

            return array(
                "EXPIRED",
                "Marked expired by"
            );


        case "REVOKED":

            return array(
                "REVOKED",
                "Revoked by"
            );


        // --------------------------------------------------
        // LOGISTICS
        // --------------------------------------------------

        case "PREPARED":

            return array(
                "PREPARED",
                "Prepared by"
            );


        case "READY_FOR_DISPATCH":

            return array(
                "READY_FOR_DISPATCH",
                "Marked ready for dispatch by"
            );


        case "PARTIALLY_DISPATCHED":

            return array(
                "PARTIALLY_DISPATCHED",
                "Partially dispatched by"
            );


        case "DISPATCHED":

            return array(
                "DISPATCHED",
                "Dispatched by"
            );


        case "IN_TRANSIT":

            return array(
                "IN_TRANSIT",
                "Set in transit by"
            );


        case "ARRIVED":

            return array(
                "ARRIVED",
                "Marked arrived by"
            );


        case "DELIVERED":

            return array(
                "DELIVERED",
                "Delivered by"
            );


        case "PARTIALLY_DELIVERED":

            return array(
                "PARTIALLY_DELIVERED",
                "Partially delivered by"
            );


        case "PARTIALLY_RECEIVED":

            return array(
                "PARTIALLY_RECEIVED",
                "Partially received by"
            );


        case "RECEIVED":

            return array(
                "RECEIVED",
                "Received by"
            );


        case "WITH_DISCREPANCY":

            return array(
                "WITH_DISCREPANCY",
                "Marked with discrepancy by"
            );


        case "DIVERTED":

            return array(
                "DIVERTED",
                "Diverted by"
            );


        case "ISSUED":

            return array(
                "ISSUED",
                "Issued by"
            );


        // --------------------------------------------------
        // TRIP / DRIVER APP
        // --------------------------------------------------

        case "PLANNED":

            return array(
                "PLANNED",
                "Planned by"
            );


        case "ASSIGNED":

            return array(
                "ASSIGNED",
                "Assigned by"
            );


        case "NOT_STARTED":

            return array(
                "NOT_STARTED",
                "Set not started by"
            );


        case "STARTED":

            return array(
                "STARTED",
                "Started by"
            );


        case "ENDED":

            return array(
                "ENDED",
                "Ended by"
            );


        // --------------------------------------------------
        // DIVERT
        // --------------------------------------------------

        case "OPEN":

            return array(
                "OPEN",
                "Opened by"
            );


        case "IMPLEMENTED":

            return array(
                "IMPLEMENTED",
                "Implemented by"
            );


        // --------------------------------------------------
        // DIGITAL SIGNATURE
        // --------------------------------------------------

        case "SIGNED":

            return array(
                "SIGNED",
                "Signed by"
            );


        case "UNSIGNED":

            return array(
                "UNSIGNED",
                "Marked unsigned by"
            );


        // --------------------------------------------------
        // COMPLETION
        // --------------------------------------------------

        case "COMPLETED":
        case "DONE":

            return array(
                "COMPLETED",
                "Completed by"
            );


        case "CLOSED":

            return array(
                "CLOSED",
                "Closed by"
            );


        case "FAILED":
        case "FAILED_DELIVERY":

            return array(
                "FAILED",
                "Marked failed by"
            );


        // --------------------------------------------------
        // CANCEL
        // --------------------------------------------------

        case "CANCELLED":
        case "CANCELED":

            return array(
                "CANCELLED",
                "Cancelled by"
            );


        // --------------------------------------------------
        // UNKNOWN STATUS
        //
        // STILL LOG IT.
        // --------------------------------------------------

        default:

            return array(
                "STATUS_CHANGED",
                "Status changed by"
            );
    }
}



// ======================================================
// INSERT HISTORY
// ======================================================

function dswdHistoryInsert(
    $config,
    $recordId,
    $recordNo,
    $actionCode,
    $actionLabel,
    $details,
    $fromStatus = "",
    $toStatus = ""
)
{
    $actor =
        dswdHistoryGetActor();


    $actionAt =
        dswdHistoryNow();


    $safeModule =
        db_addslashes(
            $config[
                "module_code"
            ]
        );


    $safeRecordNo =
        db_addslashes(
            $recordNo
        );


    $safeActionCode =
        db_addslashes(
            $actionCode
        );


    $safeActionLabel =
        db_addslashes(
            $actionLabel
        );


    $safeDetails =
        db_addslashes(
            $details
        );


    $safeFromStatus =
        db_addslashes(
            $fromStatus
        );


    $safeToStatus =
        db_addslashes(
            $toStatus
        );


    $safeUsername =
        db_addslashes(
            $actor[
                "username"
            ]
        );


    $safeActor =
        db_addslashes(
            $actor[
                "actor_name"
            ]
        );


    // --------------------------------------------------
    // DUPLICATE GUARD
    //
    // Same:
    // module
    // record
    // action
    // user
    // from status
    // to status
    //
    // within 2 seconds
    //
    // protects against accidental double-event execution.
    // --------------------------------------------------

    $duplicate =
        DBLookup(
            "
            SELECT id

            FROM transaction_history_log

            WHERE module_code =
                '" .
                    $safeModule .
                "'

            AND record_id =
                " .
                    intval(
                        $recordId
                    ) . "

            AND action_code =
                '" .
                    $safeActionCode .
                "'

            AND actor_username =
                '" .
                    $safeUsername .
                "'

            AND COALESCE(
                    from_status,
                    ''
                ) =
                '" .
                    $safeFromStatus .
                "'

            AND COALESCE(
                    to_status,
                    ''
                ) =
                '" .
                    $safeToStatus .
                "'

            AND action_at BETWEEN

                DATE_SUB(
                    '" .
                        $actionAt .
                    "',
                    INTERVAL 2 SECOND
                )

                AND

                DATE_ADD(
                    '" .
                        $actionAt .
                    "',
                    INTERVAL 2 SECOND
                )

            ORDER BY id DESC

            LIMIT 1
            "
        );


    if ($duplicate) {

        dswdHistoryDebug(
            "Duplicate blocked: " .
            $config[
                "module_code"
            ] .
            " / " .
            $recordId .
            " / " .
            $actionCode
        );

        return false;
    }


    // --------------------------------------------------
    // INSERT
    // --------------------------------------------------

    DB::Exec(
        "
        INSERT INTO transaction_history_log
        (
            module_code,
            record_id,
            record_no,

            action_code,
            action_label,
            action_details,

            from_status,
            to_status,

            acted_by,
            actor_username,
            actor_name,

            action_at
        )
        VALUES
        (
            '" .
                $safeModule .
            "',

            " .
                intval(
                    $recordId
                ) .
            ",

            '" .
                $safeRecordNo .
            "',

            '" .
                $safeActionCode .
            "',

            '" .
                $safeActionLabel .
            "',

            '" .
                $safeDetails .
            "',

            " .
            (
                trim(
                    $fromStatus
                ) != ""

                ? "'" .
                    $safeFromStatus .
                  "'"

                : "NULL"
            ) .
            ",

            " .
            (
                trim(
                    $toStatus
                ) != ""

                ? "'" .
                    $safeToStatus .
                  "'"

                : "NULL"
            ) .
            ",

            " .
            (
                intval(
                    $actor[
                        "user_id"
                    ]
                ) > 0

                ? intval(
                    $actor[
                        "user_id"
                    ]
                )

                : "NULL"
            ) .
            ",

            '" .
                $safeUsername .
            "',

            '" .
                $safeActor .
            "',

            '" .
                $actionAt .
            "'
        )
        "
    );


    dswdHistoryDebug(
        "Inserted: " .
        $config[
            "module_code"
        ] .
        " / " .
        $recordId .
        " / " .
        $actionCode
    );


    return true;
}



// ======================================================
// AFTER RECORD ADDED
//
// Covers:
// Add
// Inline Add
// Popup Add
// ======================================================

function dswdHistoryAfterAdd(
    $moduleCode,
    $keys,
    $values
)
{
    $config =
        dswdHistoryConfig(
            $moduleCode
        );


    if (!$config) {

        return false;
    }


    $recordId =
        dswdHistoryGetRecordId(
            $config,
            $keys,
            $values
        );


    if ($recordId <= 0) {

        dswdHistoryDebug(
            $moduleCode .
            ": Record ID not found after add."
        );

        return false;
    }


    $recordNo =
        dswdHistoryGetRecordNo(
            $config,
            $recordId,
            $values
        );


    // --------------------------------------------------
    // CREATED SHOULD EXIST ONLY ONCE
    // --------------------------------------------------

    $existing =
        DBLookup(
            "
            SELECT id

            FROM transaction_history_log

            WHERE module_code = '" .
                db_addslashes(
                    $moduleCode
                ) .
            "'

            AND record_id =
                " .
                    intval(
                        $recordId
                    ) . "

            AND action_code =
                'CREATED'

            LIMIT 1
            "
        );


    if ($existing) {

        return false;
    }


    // --------------------------------------------------
    // GET FIRST STATUS FOR CREATED RECORD
    // --------------------------------------------------

    $toStatus =
        "";


    foreach (
        $config[
            "status_fields"
        ] as
        $fieldName =>
        $fieldLabel
    ) {

        $value =
            $values[
                $fieldName
            ]
            ?? "";


        if (
            trim(
                (string)$value
            ) == ""
        ) {

            $value =
                DBLookup(
                    "
                    SELECT `" .
                        $fieldName .
                    "`

                    FROM `" .
                        $config[
                            "table"
                        ] .
                    "`

                    WHERE `" .
                        $config[
                            "pk"
                        ] .
                    "` = " .
                        intval(
                            $recordId
                        ) . "

                    LIMIT 1
                    "
                );
        }


        $value =
            dswdHistoryNormalizeStatus(
                $value,
                $fieldName
            );


        if ($value != "") {

            $toStatus =
                $value;

            break;
        }
    }


    $details =
        $config[
            "label"
        ] .
        " " .
        $recordNo .
        " was created.";


    return dswdHistoryInsert(
        $config,
        $recordId,
        $recordNo,
        "CREATED",
        "Created by",
        $details,
        "",
        $toStatus
    );
}



// ======================================================
// AFTER RECORD UPDATED
//
// Covers:
// Edit
// Inline Edit
// Popup Edit
//
// If one or more configured statuses changed:
// writes status history.
//
// If no status changed:
// writes one EDITED history.
// ======================================================

function dswdHistoryAfterUpdate(
    $moduleCode,
    $keys,
    $values,
    $oldvalues
)
{
    $config =
        dswdHistoryConfig(
            $moduleCode
        );


    if (!$config) {

        return false;
    }


    $recordId =
        dswdHistoryGetRecordId(
            $config,
            $keys,
            $values,
            $oldvalues
        );


    if ($recordId <= 0) {

        dswdHistoryDebug(
            $moduleCode .
            ": Record ID not found after update."
        );

        return false;
    }


    $recordNo =
        dswdHistoryGetRecordNo(
            $config,
            $recordId,
            $values,
            $oldvalues
        );


    $statusChanged =
        false;


    // --------------------------------------------------
    // CHECK EVERY CONFIGURED STATUS FIELD
    // --------------------------------------------------

    foreach (
        $config[
            "status_fields"
        ] as
        $fieldName =>
        $fieldLabel
    ) {


        $oldValue =
            dswdHistoryNormalizeStatus(
                $oldvalues[
                    $fieldName
                ]
                ?? "",
                $fieldName
            );


        // If the field was not submitted,
        // keep old value so we don't create
        // a fake status change.

        $newRawValue =
            array_key_exists(
                $fieldName,
                $values
            )

            ? $values[
                $fieldName
            ]

            : (
                $oldvalues[
                    $fieldName
                ]
                ?? ""
            );


        $newValue =
            dswdHistoryNormalizeStatus(
                $newRawValue,
                $fieldName
            );


        if (
            strtoupper(
                trim(
                    $oldValue
                )
            )
            ==
            strtoupper(
                trim(
                    $newValue
                )
            )
        ) {

            continue;
        }


        $statusChanged =
            true;


        list(
            $actionCode,
            $actionLabel
        ) =
            dswdHistoryStatusAction(
                $newValue
            );


        $details =
            $config[
                "label"
            ] .
            " " .
            $recordNo .
            " - " .
            $fieldLabel .
            " changed from " .
            (
                $oldValue != ""
                    ? $oldValue
                    : "(blank)"
            ) .
            " to " .
            (
                $newValue != ""
                    ? $newValue
                    : "(blank)"
            ) .
            ".";


        dswdHistoryInsert(
            $config,
            $recordId,
            $recordNo,
            $actionCode,
            $actionLabel,
            $details,
            $oldValue,
            $newValue
        );
    }


    // --------------------------------------------------
    // NO STATUS CHANGED = NORMAL EDIT
    // --------------------------------------------------

    if (!$statusChanged) {

        $details =
            $config[
                "label"
            ] .
            " " .
            $recordNo .
            " was edited.";


        return dswdHistoryInsert(
            $config,
            $recordId,
            $recordNo,
            "UPDATED",
            "Edited by",
            $details,
            "",
            ""
        );
    }


    return true;
}



// ======================================================
// AFTER RECORD DELETED
// ======================================================

function dswdHistoryAfterDelete(
    $moduleCode,
    $deleted_values
)
{
    $config =
        dswdHistoryConfig(
            $moduleCode
        );


    if (!$config) {

        return false;
    }


    $recordId =
        intval(
            $deleted_values[
                $config[
                    "pk"
                ]
            ]
            ?? 0
        );


    if ($recordId <= 0) {

        dswdHistoryDebug(
            $moduleCode .
            ": Record ID not found after delete."
        );

        return false;
    }


    $recordNo =
        "";


    $recordNoField =
        $config[
            "record_no"
        ];


    if (
        $recordNoField != ""
    ) {

        $recordNo =
            trim(
                (string)(
                    $deleted_values[
                        $recordNoField
                    ]
                    ?? ""
                )
            );
    }


    if ($recordNo == "") {

        $recordNo =
            $config[
                "label"
            ] .
            " #" .
            $recordId;
    }


    $fromStatus =
        "";


    foreach (
        $config[
            "status_fields"
        ] as
        $fieldName =>
        $fieldLabel
    ) {

        if (
            isset(
                $deleted_values[
                    $fieldName
                ]
            )
        ) {

            $fromStatus =
                dswdHistoryNormalizeStatus(
                    $deleted_values[
                        $fieldName
                    ],
                    $fieldName
                );

            break;
        }
    }


    $details =
        $config[
            "label"
        ] .
        " " .
        $recordNo .
        " was deleted.";


    return dswdHistoryInsert(
        $config,
        $recordId,
        $recordNo,
        "DELETED",
        "Deleted by",
        $details,
        $fromStatus,
        ""
    );
}



// ======================================================
// MANUAL ACTION
//
// Use after custom direct SQL:
//
// APPROVE
// REJECT
// RETURN
// CANCEL
// DISPATCH
// RECEIVE
// COMPLETE
// ETC.
//
// Example:
//
// dswdHistoryManualAction(
//     "DISPATCH_PLAN",
//     $recordId,
//     "APPROVED",
//     "Approved by",
//     "FOR_APPROVAL",
//     "APPROVED"
// );
// ======================================================

function dswdHistoryManualAction(
    $moduleCode,
    $recordId,
    $actionCode,
    $actionLabel,
    $fromStatus = "",
    $toStatus = "",
    $customDetails = ""
)
{
    $config =
        dswdHistoryConfig(
            $moduleCode
        );


    if (!$config) {

        return false;
    }


    $recordId =
        intval(
            $recordId
        );


    if ($recordId <= 0) {

        return false;
    }


    $recordNo =
        dswdHistoryGetRecordNo(
            $config,
            $recordId
        );


    if (
        trim(
            $customDetails
        ) != ""
    ) {

        $details =
            trim(
                $customDetails
            );

    } else {

        $verb =
            strtolower(
                str_replace(
                    " by",
                    "",
                    $actionLabel
                )
            );


        $details =
            $config[
                "label"
            ] .
            " " .
            $recordNo .
            " was " .
            $verb .
            ".";
    }


    return dswdHistoryInsert(
        $config,
        $recordId,
        $recordNo,
        strtoupper(
            trim(
                $actionCode
            )
        ),
        $actionLabel,
        $details,
        $fromStatus,
        $toStatus
    );
}



// ======================================================
// ACTION COLOR
// ======================================================

function dswdHistoryColor(
    $actionCode
)
{
    $actionCode =
        strtoupper(
            trim(
                $actionCode
            )
        );


    switch ($actionCode) {

        case "CREATED":
            return "#3f51b5";

        case "UPDATED":
            return "#ff9500";

        case "SUBMITTED":
        case "FOR_CHECKING":
            return "#ff3b30";

        case "APPROVED":
        case "CHECKED":
        case "COMPLETED":
        case "CLOSED":
        case "RECEIVED":
        case "DELIVERED":
            return "#34c759";

        case "RETURNED":
            return "#af52de";

        case "REJECTED":
        case "FAILED":
            return "#d0021b";

        case "CANCELLED":
        case "REVOKED":
        case "DELETED":
            return "#8e8e93";

        case "DISPATCHED":
        case "IN_TRANSIT":
            return "#007aff";

        default:
            return "#666666";
    }
}



// ======================================================
// ACTIVITY LOG PREVIEW
//
// Use in:
// Edit Page -> PHP Code
// ======================================================

function dswdHistoryShow(
    $moduleCode,
    $pageObject
)
{
    $config =
        dswdHistoryConfig(
            $moduleCode
        );


    if (!$config) {

        echo '
        <div class="alert alert-danger">
            History module configuration not found:
            ' .
            htmlspecialchars(
                $moduleCode
            ) .
        '
        </div>
        ';

        return;
    }


    $currentRecord =
        $pageObject->getCurrentRecord();


    if (!$currentRecord) {

        return;
    }


    $recordId =
        intval(
            $currentRecord[
                $config[
                    "pk"
                ]
            ]
            ?? 0
        );


    if ($recordId <= 0) {

        return;
    }


    // --------------------------------------------------
    // LATEST HISTORY
    // --------------------------------------------------

    $sqlHistory = "
        SELECT
            id,
            action_code,
            action_label,
            actor_name,
            actor_username,
            action_at

        FROM transaction_history_log

        WHERE module_code = '" .
            db_addslashes(
                $moduleCode
            ) .
        "'

        AND record_id = " .
            $recordId . "

        ORDER BY
            action_at DESC,
            id DESC

        LIMIT 1
    ";


    $rsHistory =
        DB::Query(
            $sqlHistory
        );


    $history =
        $rsHistory
            ? $rsHistory->fetchAssoc()
            : null;


    $activity =
        "";


    $activityColor =
        "#666666";


    $name =
        "";


    $performedBy =
        "";


    $timeDisplay =
        "";


    if ($history) {

        $activity =
            trim(
                (string)(
                    $history[
                        "action_label"
                    ]
                    ?? ""
                )
            );


        $actionCode =
            strtoupper(
                trim(
                    (string)(
                        $history[
                            "action_code"
                        ]
                        ?? ""
                    )
                )
            );


        if ($activity == "") {

            $activity =
                $actionCode;
        }


        $activityColor =
            dswdHistoryColor(
                $actionCode
            );


        $name =
            trim(
                (string)(
                    $history[
                        "actor_name"
                    ]
                    ?? ""
                )
            );


        if ($name == "") {

            $name =
                trim(
                    (string)(
                        $history[
                            "actor_username"
                        ]
                        ?? ""
                    )
                );
        }


        if ($name == "") {

            $name =
                "System";
        }


        $performedBy =
            $name;


        $actionAt =
            trim(
                (string)(
                    $history[
                        "action_at"
                    ]
                    ?? ""
                )
            );


        if ($actionAt != "") {

            try {

                $dt =
                    new DateTime(
                        $actionAt,
                        new DateTimeZone(
                            "Asia/Manila"
                        )
                    );


                $timeDisplay =
                    $dt->format(
                        "M d, Y | h:i A"
                    );

            }
            catch (Exception $e) {

                $timeDisplay =
                    $actionAt;
            }
        }
    }


    // ==================================================
    // STYLE
    // ==================================================

    echo '
    <style>

    .module-activity-log {
        width:100% !important;
        min-width:100% !important;
        max-width:none !important;
        margin:16px 0 14px 0 !important;
        padding:0 !important;
        background:#f7f7f7 !important;
        border-radius:3px !important;
        overflow:hidden !important;
        box-sizing:border-box !important;
        font-family:Arial,sans-serif !important;
    }

    .module-activity-header {
        width:100% !important;
        height:36px !important;
        display:flex !important;
        align-items:center !important;
        justify-content:space-between !important;
        padding:0 9px 0 14px !important;
        margin:0 !important;
        background:#34349a !important;
        box-sizing:border-box !important;
    }

    .module-activity-title {
        color:#ffffff !important;
        font-size:11px !important;
        font-weight:600 !important;
    }

    .btnViewAllHistory {
        height:24px !important;
        padding:0 9px !important;
        display:inline-flex !important;
        align-items:center !important;
        justify-content:center !important;
        background:#ffffff !important;
        color:#34349a !important;
        border:1px solid #d6d6d6 !important;
        border-radius:3px !important;
        font-size:9px !important;
        font-weight:500 !important;
        cursor:pointer !important;
        white-space:nowrap !important;
    }

    .module-activity-table {
        width:100% !important;
        min-width:100% !important;
        background:#f7f7f7 !important;
    }

    .module-activity-table-head,
    .module-activity-table-row {
        width:100% !important;
        display:grid !important;

        grid-template-columns:
            25%
            25%
            30%
            20% !important;

        box-sizing:border-box !important;
    }

    .module-activity-table-head {
        padding:9px 14px 3px 14px !important;
    }

    .module-activity-table-row {
        padding:2px 14px 12px 14px !important;
    }

    .module-activity-table-head > div {
        color:#666666 !important;
        font-size:10px !important;
        font-weight:400 !important;
    }

    .module-activity-table-row > div {
        color:#222222 !important;
        font-size:11px !important;
        white-space:nowrap !important;
        overflow:hidden !important;
        text-overflow:ellipsis !important;
    }

    .module-activity-empty {
        padding:14px !important;
        text-align:center !important;
        color:#888888 !important;
        font-size:10px !important;
    }

    .activity-log-full-width {
        width:100% !important;
        min-width:100% !important;
        max-width:100% !important;
        flex:0 0 100% !important;
        float:none !important;
        clear:both !important;
    }

    </style>
    ';


    // ==================================================
    // OUTPUT
    // ==================================================

    echo '

    <div class="module-activity-log">

        <div class="module-activity-header">

            <div class="module-activity-title">
                Activity Logs
            </div>

            <button
                type="button"
                class="btnViewAllHistory"

                data-module="' .
                    htmlspecialchars(
                        $moduleCode
                    ) .
                '"

                data-recordid="' .
                    intval(
                        $recordId
                    ) .
                '">

                &#8634;&nbsp;
                View All

            </button>

        </div>


        <div class="module-activity-table">

            <div class="module-activity-table-head">

                <div>
                    Activity
                </div>

                <div>
                    Name
                </div>

                <div>
                    Time
                </div>

                <div>
                    Performed by
                </div>

            </div>
    ';


    if ($history) {

        echo '

            <div class="module-activity-table-row">

                <div>

                    <span style="
                        color:' .
                            htmlspecialchars(
                                $activityColor
                            ) .
                        ';
                        font-weight:500;
                    ">

                        ' .
                            htmlspecialchars(
                                $activity
                            ) .
                        '

                    </span>

                </div>


                <div>

                    ' .
                        htmlspecialchars(
                            $name
                        ) .
                    '

                </div>


                <div>

                    ' .
                        htmlspecialchars(
                            $timeDisplay
                        ) .
                    '

                </div>


                <div>

                    ' .
                        htmlspecialchars(
                            $performedBy
                        ) .
                    '

                </div>

            </div>
        ';

    } else {

        echo '

            <div class="module-activity-empty">

                No activity history yet.

            </div>
        ';
    }


    echo '

        </div>

    </div>
    ';


    // ==================================================
    // FORCE FULL WIDTH
    // ==================================================

    echo '

    <script>

    (function () {

        function dswdFixActivityWidth() {

            var logs =
                document.querySelectorAll(
                    ".module-activity-log"
                );


            logs.forEach(
                function (log) {

                    log.style.width =
                        "100%";


                    log.style.maxWidth =
                        "none";


                    var current =
                        log.parentElement;


                    var level =
                        0;


                    while (
                        current &&
                        current.parentElement &&
                        level < 10
                    ) {

                        var parent =
                            current.parentElement;


                        var currentWidth =
                            current
                                .getBoundingClientRect()
                                .width;


                        var parentWidth =
                            parent
                                .getBoundingClientRect()
                                .width;


                        if (
                            parentWidth > 0 &&
                            currentWidth > 0 &&
                            currentWidth <
                                parentWidth * 0.75
                        ) {

                            current
                                .classList
                                .add(
                                    "activity-log-full-width"
                                );


                            current.style.width =
                                "100%";


                            current.style.maxWidth =
                                "100%";


                            current.style.flex =
                                "0 0 100%";


                            break;
                        }


                        current =
                            parent;


                        level++;
                    }
                }
            );
        }


        dswdFixActivityWidth();


        setTimeout(
            dswdFixActivityWidth,
            100
        );


        setTimeout(
            dswdFixActivityWidth,
            500
        );


        window.addEventListener(
            "resize",
            dswdFixActivityWidth
        );

    })();

    </script>
    ';
}

// ======================================================
// BACKWARD COMPATIBILITY
//
// Supports old Activity Log calls:
//
// dswdHistoryPreview(
//     "LOGISTICS_VEHICLE",
//     "id",
//     $pageObject
// );
//
// New engine internally uses:
// dswdHistoryShow()
// ======================================================

if (!function_exists("dswdHistoryPreview")) {

    function dswdHistoryPreview(
        $moduleCode,
        $pkFieldOrPageObject = null,
        $pageObject = null
    )
    {

        // --------------------------------------------------
        // OLD FORMAT:
        //
        // dswdHistoryPreview(
        //     "LOGISTICS_VEHICLE",
        //     "id",
        //     $pageObject
        // );
        // --------------------------------------------------

        if (
            $pageObject !== null &&
            is_object($pageObject)
        ) {

            dswdHistoryShow(
                $moduleCode,
                $pageObject
            );

            return;
        }


        // --------------------------------------------------
        // NEWER 2-PARAMETER FORMAT:
        //
        // dswdHistoryPreview(
        //     "LOGISTICS_VEHICLE",
        //     $pageObject
        // );
        // --------------------------------------------------

        if (
            is_object(
                $pkFieldOrPageObject
            )
        ) {

            dswdHistoryShow(
                $moduleCode,
                $pkFieldOrPageObject
            );

            return;
        }


        // --------------------------------------------------
        // GLOBAL PAGE OBJECT FALLBACK
        // --------------------------------------------------

        if (
            isset(
                $GLOBALS["pageObject"]
            )
            &&
            is_object(
                $GLOBALS["pageObject"]
            )
        ) {

            dswdHistoryShow(
                $moduleCode,
                $GLOBALS["pageObject"]
            );

            return;
        }
    }
};

?>