<?php
/**
 * Filename: email_status_handler.php
 * Author: Farhan Akhtar
 * Last Modified on : 07-11-2025
 *
 * Purpose:
 * This file handles email preprocessing logic for a CRM/helpdesk system.
 * It includes functions to:
 *
 * 1. Retrieve feature flags (e.g., Microsoft Dynamics 365 and Sticky Agent support)
 *    from the EmailAssignmentRules table.
 *    - getAssignmentFlags($conn, $db)
 *
 * 2. Retrieve a contact ID, name, and priority from the local database or 
 *    Microsoft Dynamics 365, based on the sender's email. Also updates 
 *    local cache in 'web_accounts', and updates contact name and priority 
 *    flags in 'web_email_information'.
 *    - getContactInfoWithPriority($conn, $db, $email, $interact_id, $useDynamics)
 *
 * 3. Check if an incoming email's sender is in the blocked list and mark the
 *    email as spam if found.
 *    - markEmailAsSpamIfBlocked($conn, $db, $interact_id, $email)
 *
 * 4. Detect duplicate emails by generating a SHA-256 hash from the subject and
 *    body content, and mark duplicates accordingly.
 *    - markEmailAsDuplicateIfFound($conn, $db, $email, $subject, $body, $interact_id)
 *
 * 5. Assign a "sticky agent" if the sender has previously interacted with a specific agent
 *    through reply, disposition, or case creation, and sticky assignment is enabled.
 *    The assignment now includes logic to handle agent leaves:
 *      - If the last assigned agent is currently on leave (checked against 'agent_leaves' table),
 *        reassigns the email to the configured reassignment agent before assignment.
 *    - assignStickyAgentIfFound($conn, $db, $interact_id, $email)
 *
 * 6. Classify an email using an external AI-based classification API. 
 *    The API analyzes the subject and body to determine category, department, 
 *    and other metadata, then updates the 'Classification' field in the 
 *    'web_incomingemail' table.
 *    - updateClassificationPriorityFromAPI($conn, $db, $interaction_id, $subject, $body)
 *
 * 7. Run full preprocessing on an incoming email:
 *    - Detect and mark duplicates
 *    - Check and classify as spam (blocked list)
 *    - Assign sticky agent (with leave reassignment if applicable)
 *    - Retrieve and update contact info (from CRM or local DB)
 *    - Call classification API to update category (Classification field)
 *    - Mark priority and update contact name
 *    - preprocessEmail($conn, $db, $email_id, $from_email, $subject, $body)
 *
 * Dependencies:
 * - dynamic_function.php (for Dynamics 365 OAuth token retrieval)
 * - MySQL DB connection (tables: web_email_information, web_accounts, EmailAssignmentRules, agent_leaves, web_incomingemail)
 * - Microsoft Dynamics 365 API Integration (v9.2)
 * - External Classification API (http://209.38.165.2:9096/classify)
 *
 * Description:
 * These functions automate backend processing for incoming emails to ensure 
 * normalization, deduplication, spam control, classification, prioritization, 
 * CRM enrichment, and correct agent assignment — before the ticket reaches the queue.
 */

require_once dirname(__DIR__) . "/config/app_paths.php";
require_once BIS_CAMPAIGN_ROOT . "/function/dynamics_auth.php";

// ===== Logging Config =====
$customLogFile = BIS_CAMPAIGN_ROOT . '/logs/email_process_log.txt';
$enableLogging = isset($link, $db) ? getLoggingStatusFromDB($link, $db) : false;

// Dynamics token loaded on demand inside getContactInfoWithPriority()
$baseUrl = null;
$token = null;

/**
 * Fetch enableLogging setting from EmailAssignmentRules table.
 *
 * @param mysqli $conn - DB connection
 * @param string $db - DB name
 * @return bool - true if logging is enabled
 */
function getLoggingStatusFromDB($conn, $db) {
    $stmt = $conn->prepare("SELECT enableLogging FROM {$db}.EmailAssignmentRules WHERE isActive = 1 LIMIT 1");
    if ($stmt) {
        $stmt->execute();
        $result = $stmt->get_result();
        if ($row = $result->fetch_assoc()) {
            return (int)$row['enableLogging'] === 1;
        }
        $stmt->close();
    }
    return false; // default fallback
}

/**
 * Write log message to custom log file (if enabled)
 */
function writeToLog($message, $level = 'INFO') {
    global $enableLogging, $customLogFile;
    if ($enableLogging) {
        $formatted = "[" . date('Y-m-d H:i:s') . "] [$level] $message";
        file_put_contents($customLogFile, $formatted, FILE_APPEND);
    }
}

/**
 * Fetches configuration flags from EmailAssignmentRules for:
 * - Microsoft Dynamics integration
 * - Sticky agent assignment
 *
 * @param mysqli $conn - Active DB connection
 * @param string $db - DB name
 * @return array - ['microsoftDynamics' => 0|1, 'stickyAgent' => 0|1]
 */
function getAssignmentFlags($conn, $db) {
    $flags = ['microsoftDynamics' => 0, 'stickyAgent' => 0];

    $query = "SELECT microsoftDynamics, stickyAgent FROM {$db}.EmailAssignmentRules WHERE isActive = 1 LIMIT 1";
    $stmt = $conn->prepare($query);
    if (!$stmt) {
        writeToLog("[getAssignmentFlags][$db] Failed to prepare statement: {$conn->error}\n");
        return $flags; // default values
    }

    if ($stmt->execute()) {
        $result = $stmt->get_result();
        if ($row = $result->fetch_assoc()) {
            $flags['microsoftDynamics'] = (int)$row['microsoftDynamics'];
            $flags['stickyAgent'] = (int)$row['stickyAgent'];
            writeToLog("[getAssignmentFlags][$db] Flags fetched: " . json_encode($flags) . "\n");
        }
    } else {
        writeToLog("[getAssignmentFlags][$db] Failed to execute: {$stmt->error}\n");
    }

    $stmt->close();
    return $flags;
}

/**
 * Fetch contact ID, name, and priority using local DB first.
 * If allowed (based on flag), fetch from Dynamics 365 if not found.
 * Ensures uniqueness in web_accounts.
 * Updates contact name and priority in web_email_information.
 *
 * @param mysqli $conn - Active DB connection
 * @param string $db - DB name
 * @param string $email - Sender email address
 * @param int $interact_id - Email ID (for updating web_email_information)
 * @param bool $useDynamics - Whether to query Dynamics if not found locally
 * @return array|null - ['contactid' => ..., 'fullname' => ..., 'ispriority' => 1|0] or null
 */
function getContactInfoWithPriority($conn, $db, $email, $interact_id, $useDynamics) {
    global $token, $baseUrl;

    $email = trim($email);
    if (empty($email)) return null;

    $contactid = '';
    $fullname = '';
    $ispriority = 0;

    // 1. Check local DB for contact info
    $stmt = $conn->prepare("SELECT userid AS contactid, fname AS fullname, priority_user FROM $db.web_accounts WHERE email = ?");
    if ($stmt) {
        $stmt->bind_param("s", $email);
        $stmt->execute();
        $stmt->bind_result($contactid, $fullname, $priority_user);
        if ($stmt->fetch()) {
            $ispriority = (int)$priority_user;
            writeToLog("[getContactInfoWithPriority][$db] Found locally: $fullname ($contactid), Priority: $ispriority for $email\n");
        }
        $stmt->close();
    }

    // 2. If contact not found locally, and Dynamics is allowed
    if (empty($contactid) && $useDynamics) {
        writeToLog("[getContactInfoWithPriority][$db] Fetching from Dynamics for $email\n");

        $dynConfig = loadDynamicsConfig($conn, $db);
        if (!$dynConfig) {
            writeToLog("[getContactInfoWithPriority][$db] Dynamics config not found.\n");
            return null;
        }
        $baseUrl = $dynConfig['resource'];
        $token = getAccessToken($dynConfig);
        if (!$token) {
            writeToLog("[getContactInfoWithPriority][$db] Dynamics token unavailable.\n");
            return null;
        }

        $escapedEmail = str_replace("'", "''", $email);
        $filter = "emailaddress1 eq '$escapedEmail'";
        $url = "$baseUrl/api/data/v9.2/contacts?\$select=contactid,fullname,telephone1&\$filter=" . urlencode($filter);

        $headers = [
            "Authorization: Bearer $token",
            "Accept: application/json",
            "Content-Type: application/json"
        ];

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        writeToLog("[getContactInfoWithPriority][$db] Dynamics 365 request for $email - HTTP $httpCode\n");

        if ($httpCode == 200) {
            $data = json_decode($response, true);
            if (!empty($data['value'][0]['contactid'])) {
                $contactid = $data['value'][0]['contactid'];
                $fullname = $data['value'][0]['fullname'] ?? '';
                $phone = $data['value'][0]['telephone1'] ?? '';

                // Insert or update into web_accounts
                $insertStmt = $conn->prepare("
                    INSERT INTO $db.web_accounts (email, userid, fname, phone, createddate)
                    VALUES (?, ?, ?, ?, NOW())
                    ON DUPLICATE KEY UPDATE 
                        userid = VALUES(userid),
                        fname = VALUES(fname),
                        phone = VALUES(phone)
                ");
                if ($insertStmt) {
                    $insertStmt->bind_param("ssss", $email, $contactid, $fullname, $phone);
                    $insertStmt->execute();
                    $insertStmt->close();
                    writeToLog("[getContactInfoWithPriority][$db] Inserted/Updated local DB: $fullname ($contactid)\n");
                }
            }
        } else {
            writeToLog("[getContactInfoWithPriority][$db] Dynamics error: $response\n");
        }
    }

    // 3. Skip if still no contact info found
    if (empty($contactid) && empty($fullname)) {
        writeToLog("[getContactInfoWithPriority][$db] Contact not found in DB or Dynamics. Skipping update for $email\n");
        return null;
    }

    // 4. Update email information table
    $updateStmt = $conn->prepare("UPDATE $db.web_email_information SET v_name = ?, ispriority = ? WHERE EMAIL_ID = ?");
    if ($updateStmt) {
        $updateStmt->bind_param("sii", $fullname, $ispriority, $interact_id);
        if ($updateStmt->execute()) {
            writeToLog("[$interact_id] Updated contact name and priority for $email\n");
        } else {
            writeToLog("[$interact_id] Failed to update contact info: {$updateStmt->error}\n");
        }
        $updateStmt->close();
    }

    return ['contactid' => $contactid, 'fullname' => $fullname, 'ispriority' => $ispriority];
}

/**
 * Checks if an email is listed in EmailAssignmentRules.blockedMails and marks it as spam if found.
 *
 * @param mysqli $conn - Database connection
 * @param string $db - Database name
 * @param int $interact_id - Email ID
 * @param string $email - Email address to check
 * @return string - Status message
 */
function markEmailAsSpamIfBlocked($conn, $db, $interact_id, $email) {
    $email = strtolower(trim($email));
    if (empty($email)) {
        writeToLog("[BlockCheck][$interact_id] Email is blank.\n");
        return "Email is required.";
    }

    // 1. Fetch blockedMails list from EmailAssignmentRules
    $stmt = $conn->prepare("SELECT blockedMails FROM {$db}.EmailAssignmentRules WHERE isActive = 1 LIMIT 1");
    if (!$stmt) {
        writeToLog("[BlockCheck][$interact_id] Failed to prepare EmailAssignmentRules query: {$conn->error}\n");
        return "DB error during blocked check.";
    }

    $stmt->execute();
    $stmt->bind_result($blockedMails);
    if ($stmt->fetch()) {
        $stmt->close();

        $blockedList = array_map('trim', explode(',', strtolower($blockedMails)));

        if (in_array($email, $blockedList)) {
            // 2. Mark email as spam
            $update = $conn->prepare("UPDATE {$db}.web_email_information SET classification = 3, visibility = 2 WHERE EMAIL_ID = ?");
            if ($update) {
                $update->bind_param("i", $interact_id);
                if ($update->execute()) {
                    writeToLog("[BlockCheck][$interact_id] $email marked as spam via EmailAssignmentRules.\n");
                    return "Email '$email' marked as spam (classification = 3).";
                } else {
                    writeToLog("[BlockCheck][$interact_id] Failed to mark $email as spam: {$update->error}\n");
                    return "Failed to mark as spam: {$update->error}";
                }
            } else {
                writeToLog("[BlockCheck][$interact_id] Prepare failed for update statement: {$conn->error}\n");
                return "Prepare failed for update.";
            }
        } else {
            writeToLog("[BlockCheck][$interact_id] $email not found in blockedMails list.\n");
            return "Email '$email' not in blocked list. No action taken.";
        }
    } else {
        $stmt->close();
        writeToLog("[BlockCheck][$interact_id] No active EmailAssignmentRules found.\n");
        return "No blocked list available.";
    }
}

/**
 * Detects if the incoming email is a duplicate based on subject + body hash
 * - If a match is found, marks it as duplicate and sets parent_id
 * - If no match is found, saves hash and marks it original
 *
 * @param mysqli $conn - DB connection
 * @param string $db - Database name
 * @param string $email - Sender email
 * @param string $subject - Subject line
 * @param string $body - Email content
 * @param int $interact_id - Current email record ID
 * @return bool - true if marked duplicate, false if original or failed
 */
function markEmailAsDuplicateIfFound($conn, $db, $email, $subject, $body, $interact_id) {
    writeToLog("[$interact_id] [DuplicateCheck] Starting duplicate check\n");

    // Step 1: Normalize and sanitize input
    $email   = trim($email);
    $subject = strtolower(trim($subject));
    $body    = strtolower(trim($body));

    writeToLog("[$interact_id] [DuplicateCheck] Inputs - Email: '$email', Subject: '$subject'\n");
    writeToLog("[$interact_id] [DuplicateCheck] Body length: " . strlen($body) . ", Preview: " . substr($body, 0, 5) . "\n");

    // Step 2: Generate hash
    $stringToHash = str_replace(" ", "", "$subject|$body");
    $email_hash = hash("sha256", $stringToHash);
    writeToLog("[$interact_id] [DuplicateCheck] Generated hash: $email_hash\n");

    // Step 3: Look for existing mail with same hash, excluding current email
    $stmt = $conn->prepare("
        SELECT EMAIL_ID 
        FROM $db.web_email_information 
        WHERE emailHash = ? AND EMAIL_ID != ?
        ORDER BY EMAIL_ID ASC 
        LIMIT 1
    ");

    if (!$stmt) {
        writeToLog("[$interact_id] [DuplicateCheck] Failed to prepare SELECT query: {$conn->error}\n");
        return false;
    }

    $stmt->bind_param("si", $email_hash, $interact_id);
    $stmt->execute();
    $result = $stmt->get_result();
    $matched = $result->num_rows;

    writeToLog("[$interact_id] [DuplicateCheck] Rows matched for hash (excluding self): $matched\n");

    if ($matched > 0) {
        // Duplicate found
        $row = $result->fetch_assoc();
        $original_uid = $row['EMAIL_ID'];

        $update = $conn->prepare("UPDATE $db.web_email_information SET isduplicate = 1, parent_id = ?, emailHash = ? WHERE EMAIL_ID = ?");
        $update->bind_param("isi", $original_uid, $email_hash, $interact_id);

        if ($update->execute()) {
            writeToLog("[$interact_id] [DuplicateCheck] Marked as duplicate of $original_uid.\n");
            return true;
        } else {
            writeToLog("[$interact_id] [DuplicateCheck] Failed to update duplicate record: {$update->error}\n");
            return false;
        }

    } else {
        // No duplicate found, mark as original
        $update = $conn->prepare("UPDATE $db.web_email_information SET isduplicate = 0, parent_id = ?, emailHash = ? WHERE EMAIL_ID = ?");
        $update->bind_param("isi", $interact_id, $email_hash, $interact_id);

        if ($update->execute()) {
            writeToLog("[$interact_id] [DuplicateCheck] Marked as original email. Hash saved.\n");
            return false;
        } else {
            writeToLog("[$interact_id] [DuplicateCheck] Failed to update original record: {$update->error}\n");
            return false;
        }
    }
}


/**
 * Assigns a new email to the last agent who interacted with the same sender
 * via reply, disposition, or case creation.
 *
 * @param mysqli $conn - DB connection
 * @param string $db - Database name
 * @param int $interact_id - New EMAIL_ID to assign
 * @param string $email - Sender email
 * @return bool - True if assignment successful, false otherwise
 */
// function assignStickyAgentIfFound($conn, $db, $interact_id, $email) {
//     $email = trim($email);

//     if (empty($email)) {
//         writeToLog("[$interact_id] [StickyAgent] Email is blank.\n");
//         return false;
//     }

//     // Step 1: Find last handled mail by agent for same email
//     $stmt = $conn->prepare("SELECT agent_assigned, EMAIL_ID 
//         FROM $db.web_email_information 
//         WHERE v_fromemail = ? 
//           AND (email_replay_status = 1 OR is_disposed = 1 OR ICASEID > 0) 
//           AND agent_assigned IS NOT NULL AND agent_assigned != '' 
//         ORDER BY EMAIL_ID DESC 
//         LIMIT 1
//     ");
//     $stmt->bind_param("s", $email);
//     $stmt->execute();
//     $result = $stmt->get_result();

//     if ($result->num_rows > 0) {
//         // Previous agent found, assign to them
//         $row = $result->fetch_assoc();
//         $last_agent = $row['agent_assigned'];
//         $last_email_id = $row['EMAIL_ID'];

//         $now = date('Y-m-d H:i:s');
//         $assigned_by = 1; // system user

//         $update = $conn->prepare("
//             UPDATE $db.web_email_information 
//             SET agent_assigned = ?, assigned_date = ?, assigned_by = ?, visibility = 1
//             WHERE EMAIL_ID = ?
//         ");
//         $update->bind_param("ssii", $last_agent, $now, $assigned_by, $interact_id);

//         if ($update->execute()) {
//             writeToLog("[$interact_id] [StickyAgent] Assigned to '$last_agent' from EMAIL_ID $last_email_id\n");
//             return true;
//         } else {
//             writeToLog("[$interact_id] [StickyAgent] Assignment update failed: {$update->error}\n");
//             return false;
//         }

//     } else {
//         //  No previous agent found, set visibility = 2
//         $update = $conn->prepare("
//             UPDATE $db.web_email_information 
//             SET visibility = 2 
//             WHERE EMAIL_ID = ?
//         ");
//         $update->bind_param("i", $interact_id);
//         $update->execute();

//         writeToLog("[$interact_id] [StickyAgent] No matching record. Set visibility = 2\n");
//         return false;
//     }
// }


/**
 * Helper function to check if an agent is on leave and return the reassigned agent.
 *
 * @param mysqli $conn - Database connection
 * @param string $db - Database name
 * @param string $agent_id - Original agent ID to check leave status
 * @return string|false - Returns reassigned agent ID if on leave, false otherwise
 */
function getReassignedAgentIfOnLeave($conn, $db, $agent_id) {
    $today = date('Y-m-d H:i:s');

    $query = "SELECT reassign_to_agent FROM {$db}.agent_leave
              WHERE agent_id = ? AND is_deleted = 0
              AND start_date <= ? AND end_date >= ?
              LIMIT 1";
    $stmt = $conn->prepare($query);
    if (!$stmt) {
        writeToLog("[getReassignedAgentIfOnLeave] Failed to prepare statement: {$conn->error}");
        return false;
    }

    $stmt->bind_param("sss", $agent_id, $today, $today);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($row = $result->fetch_assoc()) {
        writeToLog("Agent $agent_id is on leave; reassigned to " . $row['reassign_to_agent']);
        $stmt->close();
        return $row['reassign_to_agent'];
    }

    $stmt->close();
    return false;
}

/**
 * Assigns a new email to the last agent who interacted with the same sender,
 * or to a reassigned agent if the original agent is on leave.
 *
 * @param mysqli $conn - Database connection
 * @param string $db - Database name
 * @param int $interact_id - New EMAIL_ID to assign
 * @param string $email - Sender email
 * @return bool - True if assignment successful, false otherwise
 */
function assignStickyAgentIfFound($conn, $db, $interact_id, $email) {
    $email = trim($email);

    if (empty($email)) {
        writeToLog("[$interact_id] [StickyAgent] Email is blank.\n");
        return false;
    }

    // Step 1: Find last handled mail by agent for same email
    $stmt = $conn->prepare("SELECT agent_assigned, EMAIL_ID,group_assigned
        FROM $db.web_email_information 
        WHERE v_fromemail = ? 
          AND (email_replay_status = 1 OR is_disposed = 1 OR ICASEID > 0) 
          AND ((agent_assigned IS NOT NULL AND agent_assigned != '') || (group_assigned IS NOT NULL AND group_assigned != '' ))
        ORDER BY EMAIL_ID DESC 
        LIMIT 1");
    if (!$stmt) {
        writeToLog("[$interact_id] [StickyAgent] Failed to prepare query: {$conn->error}\n");
        return false;
    }
    $stmt->bind_param("s", $email);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows > 0) {
        // Previous agent found, assign to them or reassigned agent if on leave
        $row = $result->fetch_assoc();
        $last_agent = $row['agent_assigned'];
        $last_queue = $row['group_assigned'];
        $last_email_id = $row['EMAIL_ID'];

        if(!empty($last_agent)){
            // Check if agent on leave
            $reassignedAgent = getReassignedAgentIfOnLeave($conn, $db, $last_agent);
            $agentToAssign = $reassignedAgent !== false ? $reassignedAgent : $last_agent;

            $now = date('Y-m-d H:i:s');
            $assigned_by = 1; // system user ID or identifier

            $update = $conn->prepare("UPDATE $db.web_email_information 
                SET agent_assigned = ?, assigned_date = ?, assigned_by = ?, visibility = 1
                WHERE EMAIL_ID = ?");
            if (!$update) {
                writeToLog("[$interact_id] [StickyAgent] Failed to prepare update query: {$conn->error}\n");
                return false;
            }
            $update->bind_param("ssii", $agentToAssign, $now, $assigned_by, $interact_id);

            if ($update->execute()) {
                writeToLog("[$interact_id] [StickyAgent] Assigned to '$agentToAssign' from EMAIL_ID $last_email_id\n");
                return true;
            } else {
                writeToLog("[$interact_id] [StickyAgent] Assignment update failed: {$update->error}\n");
                return false;
            }
        }else if(!empty($last_queue)){

            $now = date('Y-m-d H:i:s');
            $assigned_by = 1; // system user ID or identifier

            $update = $conn->prepare("UPDATE $db.web_email_information 
                SET group_assigned = ?, assigned_date = ?, assigned_by = ?, visibility = 1
                WHERE EMAIL_ID = ?");
            if (!$update) {
                writeToLog("[$interact_id] [StickyAgent] Failed to prepare update query: {$conn->error}\n");
                return false;
            }
            $update->bind_param("ssii", $last_queue, $now, $assigned_by, $interact_id);

            if ($update->execute()) {
                writeToLog("[$interact_id] [StickyAgent] Assigned to last_queue :'$last_queue' from EMAIL_ID $last_email_id\n");
                return true;
            } else {
                writeToLog("[$interact_id] [StickyAgent] Assignment update failed: {$update->error}\n");
                return false;
            }
        }
        
    } else {
        // No previous agent found, set visibility = 2
        $update = $conn->prepare("UPDATE $db.web_email_information SET visibility = 2 WHERE EMAIL_ID = ?");
        if (!$update) {
            writeToLog("[$interact_id] [StickyAgent] Failed to prepare visibility update query: {$conn->error}\n");
            return false;
        }
        $update->bind_param("i", $interact_id);
        $update->execute();

        writeToLog("[$interact_id] [StickyAgent] No matching record. Set visibility = 2\n");
        return false;
    }
}

/**
 * Call classification API and update only Classification in DB.
 * Logs full response details including category, department, priority, and request type.
 *
 * @param mysqli $conn
 * @param string $db
 * @param int $interaction_id
 * @param string $subject
 * @param string $body
 * @return array
 */
function updateClassificationPriorityFromAPI($conn, $db, $interaction_id, $subject, $body) {
    $apiUrl = "http://209.38.165.2:9096/classify";

    $payload = json_encode([
        'subject' => $subject,
        'body' => $body
    ]);

    $ch = curl_init($apiUrl);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_TIMEOUT => 15
    ]);

    $response = curl_exec($ch);
    $error = curl_error($ch);
    curl_close($ch);

    if ($error) {
        writeToLog("[$interaction_id] Classification API Error: $error\n");
        return ['status' => 'error', 'message' => $error];
    }

    writeToLog("[$interaction_id] Classification API Raw Response: $response\n");

    $data = json_decode($response, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        writeToLog("[$interaction_id] Invalid JSON from Classification API: $response\n");
        return ['status' => 'invalid_response'];
    }

    // If successful classification response
    if (!empty($data['status']) && strtolower($data['status']) === 'success' && !empty($data['id'])) {
        $classificationId = (int)$data['id'];

        // Log all extra details for reference
        $category    = $data['category'] ?? 'N/A';
        $department  = $data['department'] ?? 'N/A';
        $priority    = $data['ispriority'] ?? 0;   // default 0 if not provided
        $requestType = $data['request_type'] ?? 'N/A';
        $keywords    = isset($data['keywords']) ? implode(', ', $data['keywords']) : 'N/A';
        $matchCount  = $data['match_count'] ?? 'N/A';

        writeToLog("[$interaction_id] Classification API Parsed:
            ID: $classificationId
            Category: $category
            Department: $department
            Priority: $priority
            Request Type: $requestType
            Match Count: $matchCount
            Keywords: $keywords
        \n");

        // Update classification and priority in DB
        $stmt = mysqli_prepare($conn, "UPDATE $db.web_email_information 
                                    SET classification = ?, ispriority = ? 
                                    WHERE EMAIL_ID = ?");

        mysqli_stmt_bind_param($stmt, 'iii', $classificationId, $priority, $interaction_id);
        $success = mysqli_stmt_execute($stmt);
        mysqli_stmt_close($stmt);

        if ($success) {
            writeToLog("[$interaction_id] DB updated successfully - Classification: $classificationId, Priority: $priority\n");
            return ['status' => 'success', 'classification_id' => $classificationId];
        } else {
            writeToLog("[$interaction_id] Failed to update DB: " . mysqli_error($conn) . "\n");
            return ['status' => 'db_error'];
        }
    }


    writeToLog("[$interaction_id] Classification API returned no valid ID or failed status. Response: " . $response . "\n");
    return ['status' => 'no_id'];
}

/**
 * Preprocess a new incoming email: check duplicates, spam, sticky agent, CRM contact, and priority.
 *
 * @param mysqli $conn - DB connection
 * @param string $db - DB name
 * @param int $email_id - New email's EMAIL_ID
 * @param string $from_email - Sender's email
 * @param string $subject - Email subject
 * @param string $body - Email body/content
 * @return array - Summary of actions performed
 */
// function preprocessEmail($conn, $db, $interaction_id, $from_email, $subject, $body) {

//     writeToLog("[$interaction_id] ==== START PREPROCESSING EMAIL ====\n");

//     // 1. Load assignment flags (Dynamics + Sticky Agent)
//     $flags = getAssignmentFlags($conn, $db);
//     $useDynamics = $flags['microsoftDynamics'] ?? 0;
//     $useStickyAgent = $flags['stickyAgent'] ?? 0;

//     writeToLog("[$interaction_id] Assignment Flags - Dynamics: $useDynamics, StickyAgent: $useStickyAgent\n");

//     // 2. Check for duplicate
//     $isDuplicate = markEmailAsDuplicateIfFound($conn, $db, $from_email, $subject, $body, $interaction_id);
//     writeToLog("[$interaction_id] Duplicate status: " . ($isDuplicate ? "Yes" : "No") . "\n");


//     // 3. Blocked/Spam check
//     $blockStatus = markEmailAsSpamIfBlocked($conn, $db, $interaction_id, $from_email);
//     $isSpam = strpos($blockStatus, 'marked as spam') !== false;
//     writeToLog("[$interaction_id] Blocked Check: $blockStatus\n");

//     // Optional Early Exit: Skip remaining processing if spam
//     // if ($isSpam) {
//     //     writeToLog("[$interaction_id] Email marked as spam skipping CRM and agent assignment.\n");
//     //     return [
//     //         'email_id'      => $interaction_id,
//     //         'is_duplicate'  => $isDuplicate,
//     //         'is_spam'       => true,
//     //         'is_priority'   => false,
//     //         'sticky_agent'  => false,
//     //         'crm_updated'   => false,
//     //         'crm_contactid' => '',
//     //         'crm_fullname'  => '',
//     //         'from_email'    => $from_email
//     //     ];
//     // }

//     // 4. Sticky agent assignment (conditionally)
//     $stickyAssigned = false;
//     if ($useStickyAgent) {
//         $stickyAssigned = assignStickyAgentIfFound($conn, $db, $interaction_id, $from_email);
//         writeToLog("[$interaction_id] Sticky Agent: " . ($stickyAssigned ? "Assigned" : "Not assigned") . "\n");
//     } else {
//         writeToLog("[$interaction_id] Sticky Agent assignment is disabled via config.\n");
//     }

//     // 5. Get CRM contact info and priority (local or Dynamics)
//     // $contactInfo = getContactInfoWithPriority($conn, $db, $from_email, $interaction_id, $useDynamics);
//     // $contactId = $contactInfo['contactid'] ?? '';
//     // $contactName = $contactInfo['fullname'] ?? '';
//     // $prioritySet = ($contactInfo['ispriority'] ?? 0) === 1;

//     // if (!$useDynamics) {
//     //     writeToLog("[$interaction_id] CRM Dynamics fetch is disabled via config.\n");
//     // }

//     // writeToLog("[$interaction_id] Contact Info: " . ($contactId ? "$contactName ($contactId)" : "Not found") . ", Priority: " . ($prioritySet ? "Yes" : "No") . "\n");


//     // 6. Final Result Summary
//     $result = [
//         'email_id'      => $interaction_id,
//         'is_duplicate'  => $isDuplicate,
//         'is_spam'       => $isSpam,
//         // 'is_priority'   => $prioritySet,
//         'sticky_agent'  => $stickyAssigned,
//         // 'crm_contactid' => $contactId,
//         // 'crm_fullname'  => $contactName,
//         'from_email'    => $from_email
//     ];

//     writeToLog("[$interaction_id] Return Summary: " . json_encode($result) . "\n");
//     writeToLog("[$interaction_id] ==== END PREPROCESSING EMAIL ====\n");

//     return $result;
// }

/**
 * Preprocess a new incoming email: check duplicates, spam, sticky agent, CRM contact, and priority.
 * Also performs classification using an external API.
 *
 * @param mysqli $conn - DB connection
 * @param string $db - DB name
 * @param int $interaction_id - Email ID
 * @param string $from_email - Sender's email
 * @param string $subject - Email subject
 * @param string $body - Email body/content
 * @return array - Summary of actions performed
 */
function preprocessEmail($conn, $db, $interaction_id, $from_email, $subject, $body) {

    writeToLog("[$interaction_id] ==== START PREPROCESSING EMAIL ====\n");

    // 1. Load assignment flags (Dynamics + Sticky Agent)
    $flags = getAssignmentFlags($conn, $db);
    $useDynamics = $flags['microsoftDynamics'] ?? 0;
    $useStickyAgent = $flags['stickyAgent'] ?? 0;

    writeToLog("[$interaction_id] Assignment Flags - Dynamics: $useDynamics, StickyAgent: $useStickyAgent\n");

    // 2. Check for duplicate
    $isDuplicate = markEmailAsDuplicateIfFound($conn, $db, $from_email, $subject, $body, $interaction_id);
    writeToLog("[$interaction_id] Duplicate status: " . ($isDuplicate ? "Yes" : "No") . "\n");

    // 3. Blocked/Spam check
    $blockStatus = markEmailAsSpamIfBlocked($conn, $db, $interaction_id, $from_email);
    $isSpam = strpos($blockStatus, 'marked as spam') !== false;
    writeToLog("[$interaction_id] Blocked Check: $blockStatus\n");

    // 4. Sticky agent assignment (conditionally)
    $stickyAssigned = false;
    if ($useStickyAgent) {
        $stickyAssigned = assignStickyAgentIfFound($conn, $db, $interaction_id, $from_email);
        writeToLog("[$interaction_id] Sticky Agent: " . ($stickyAssigned ? "Assigned" : "Not assigned") . "\n");
    } else {
        writeToLog("[$interaction_id] Sticky Agent assignment is disabled via config.\n");
    }

    // 5. Classification using external API
    $classificationResult = updateClassificationPriorityFromAPI($conn, $db, $interaction_id, $subject, $body);
    writeToLog("[$interaction_id] Classification Result: " . json_encode($classificationResult) . "\n");

    // 6. Final Result Summary
    $result = [
        'email_id'       => $interaction_id,
        'is_duplicate'   => $isDuplicate,
        'is_spam'        => $isSpam,
        'sticky_agent'   => $stickyAssigned,
        'classification' => $classificationResult['classification_id'] ?? null,
        'classification_status' => $classificationResult['status'] ?? 'failed',
        'from_email'     => $from_email
    ];

    writeToLog("[$interaction_id] Return Summary: " . json_encode($result) . "\n");
    writeToLog("[$interaction_id] ==== END PREPROCESSING EMAIL ====\n");

    return $result;
}

/**
 * Check tbl_blocked_mailid and EmailAssignmentRules; mark email as spam when blocked.
 */
function updateClassificationIfBlocked($conn, $db, $interact_id, $email) {
    $email = strtolower(trim($email));
    if (empty($email)) {
        return ['blocked' => false, 'message' => 'Email is required.'];
    }

    $stmt = $conn->prepare("SELECT emailid FROM {$db}.tbl_blocked_mailid WHERE LOWER(emailid) = ? LIMIT 1");
    if ($stmt) {
        $stmt->bind_param('s', $email);
        $stmt->execute();
        $stmt->store_result();
        if ($stmt->num_rows > 0) {
            $stmt->close();
            $update = $conn->prepare("UPDATE {$db}.web_email_information SET classification = 3, visibility = 2 WHERE EMAIL_ID = ?");
            if ($update) {
                $update->bind_param('i', $interact_id);
                $update->execute();
                $update->close();
            }
            writeToLog("[BlockCheck][$interact_id] $email marked as spam via tbl_blocked_mailid.\n");
            return ['blocked' => true, 'message' => "Email '$email' marked as spam (blocked list)."];
        }
        $stmt->close();
    }

    $msg = markEmailAsSpamIfBlocked($conn, $db, $interact_id, $email);
    $blocked = stripos($msg, 'marked as spam') !== false;
    return ['blocked' => $blocked, 'message' => $msg];
}

/**
 * Resolve contact ID from local CRM or Dynamics 365 (when enabled).
 */
function getContactID_Dynamics365($conn, $db, $email, $interact_id = 0) {
    $flags = getAssignmentFlags($conn, $db);
    $useDynamics = !empty($flags['microsoftDynamics']);
    $info = getContactInfoWithPriority($conn, $db, $email, $interact_id, $useDynamics);
    return $info['contactid'] ?? null;
}

/**
 * Link Dynamics/local contact ID on web_accounts when userid is not yet set.
 */
function updateCustomerIdIfBlank($conn, $db, $email, $contactid) {
    $email = trim($email);
    $contactid = trim($contactid);
    if ($email === '' || $contactid === '') {
        return false;
    }

    $stmt = $conn->prepare(
        "UPDATE {$db}.web_accounts SET userid = ?
         WHERE email = ? AND (userid IS NULL OR userid = '' OR userid = '0')"
    );
    if (!$stmt) {
        writeToLog("[updateCustomerIdIfBlank] Prepare failed: {$conn->error}\n");
        return false;
    }
    $stmt->bind_param('ss', $contactid, $email);
    $ok = $stmt->execute();
    $stmt->close();
    return $ok;
}