<?php
// chatbot_meeting.php

// Set a higher maximum execution time if necessary
//set_time_limit(120); // 120 seconds

// Enable error reporting for debugging (disable in production)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Log script start
//file_put_contents(__DIR__ . '/error_log.txt', date('[Y-m-d H:i:s] ') . "Script started." . PHP_EOL, FILE_APPEND);

// Set the content type to JSON
header('Content-Type: application/json');
/* header('Access-Control-Allow-Origin: https://cherry21470635.brizy.site'); // Specific domain for security
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization"); */
header('Access-Control-Allow-Credentials: true'); 
//header("Cache-Control: no-cache, must-revalidate"); //HTTP 1.1
  //header("Pragma: no-cache"); //HTTP 1.0
  //header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past

  //or, if you DO want a file to cache, use:
header("Cache-Control: max-age=20000"); //30days (60sec * 60min * 24hours * 30days)

if (isset($_SERVER['HTTP_CACHE_CONTROL'])) {
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Page Chatbot_meeting.php Start HTTP_CACHE_CONTROL Set." . print_r($_SERVER['HTTP_CACHE_CONTROL'], true) . " | " . print_r($_SERVER['REQUEST_METHOD'], true) . PHP_EOL, FILE_APPEND);
} else {
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Page Chatbot_meeting.php Start." . print_r($_SERVER['REQUEST_METHOD'], true) . PHP_EOL, FILE_APPEND);
}

session_set_cookie_params([
    'SameSite' => 'None',
    'Secure' => true,  // This requires HTTPS
]);

// Start the session to store threadId and assistantName
session_start();

if (!isset($_SESSION["visits"])) {
    $_SESSION["visits"] = 0;
}

$_SESSION["visits"] = $_SESSION["visits"] + 1;

if ($_SESSION["visits"] > 1) {
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] SESSION variable visits moer than one: " . print_r($_SESSION["visits"], true) . PHP_EOL, FILE_APPEND);
} else {
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] SESSION variable visits less than 2." . print_r($_SESSION["visits"], true) . PHP_EOL, FILE_APPEND);
}

// Detect if the page was refreshed
if (isset($_SERVER['HTTP_CACHE_CONTROL'])) {
    $pageWasRefreshed = ( $_SERVER['HTTP_CACHE_CONTROL'] === 'max-age=0' || $_SERVER['HTTP_CACHE_CONTROL'] == 'no-cache' ) || $_SERVER['REQUEST_METHOD'] === 'POST';
} else {
   $pageWasRefreshed = $_SERVER['REQUEST_METHOD'] === 'POST';
   $_SERVER['HTTP_CACHE_CONTROL'] = 'max-age=0';
}

//$pageWasRefreshed = isset($_SERVER['HTTP_CACHE_CONTROL']) && ($_SERVER['HTTP_CACHE_CONTROL'] === 'max-age=0' || $_SERVER['HTTP_CACHE_CONTROL'] == 'no-cache');

if ($pageWasRefreshed) {
    // Reset trigger_contact on page refresh
    $_SESSION['trigger_contact'] = false;
    $_SESSION["visits"] = 0;
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Page refreshed: trigger_contact reset." . PHP_EOL, FILE_APPEND);
}

if (isset($_GET['action']) && $_GET['action'] === 'poll_trigger') {
    // Check if polling is running
    if (!isset($_SESSION['polling_running'])) {
        $_SESSION['polling_running'] = true;

        // Polling interval and loop
        $pollingInterval = 2;
        set_time_limit(0); // Prevent script timeout

        while (true) {
            $triggerContact = $_SESSION['trigger_contact'] ?? false;

            // Log and process the trigger_contact session variable
            file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] poll_trigger Endpoint - Trigger Contact: " . print_r($triggerContact, true) . PHP_EOL, FILE_APPEND);

            // If the contact is triggered, send it and reset the session
            if ($triggerContact) {
                echo json_encode(['trigger_contact' => true]);

                // Reset trigger_contact immediately after sending it
                $_SESSION['trigger_contact'] = false;
                file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Resetting trigger_contact in session after response\n", FILE_APPEND);
                break; // Exit the loop once handled
            }

            // Sleep for the polling interval (2 seconds)
            sleep($pollingInterval);
        }

        // Mark polling as complete
        $_SESSION['polling_running'] = false;
    } else {
        // Polling is already running, send the current status
        echo json_encode(['trigger_contact' => $_SESSION['trigger_contact'] ?? false]);
    }

    exit;
}

// Disable implicit output buffering
while (ob_get_level() > 0) {
    ob_end_flush();
}
ob_implicit_flush(true);

// Include the required configuration and function files
require_once __DIR__ . '/config.php'; // Contains OPENAI_API_KEY constant
require_once __DIR__ . '/assistant_config.php'; // Contains assistant configuration
require_once __DIR__ . '/functions.php'; // Include the assistant functions

// Update the Composer autoloader path based on the new folder structure
require __DIR__ . '/../orhanerday-open-ai/vendor/autoload.php'; // Adjusted path

use Orhanerday\OpenAi\OpenAi;

// Include the assistant configuration
$assistantConfig = require __DIR__ . '/assistant_config.php';

// Function to fetch assistant details including vector store ID
function getAssistantDetails($assistantId)
{
    global $open_ai;

    $assistantDetailsResponse = $open_ai->retrieveAssistant($assistantId);
    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Retrieve Assistant Response: " . $assistantDetailsResponse . PHP_EOL, FILE_APPEND);

    $assistantDetails = json_decode($assistantDetailsResponse, true);

    if (!isset($assistantDetails['tool_resources']['file_search']['vector_store_ids']) || empty($assistantDetails['tool_resources']['file_search']['vector_store_ids'])) {
        throw new Exception('No vector store IDs found for this assistant.');
    }

    // Assuming there's only one vector store for this use case
    return $assistantDetails['tool_resources']['file_search']['vector_store_ids'][0];
}

// Function to handle function calls when run requires action
function handleFunctionCalls($threadId, $runId, $requiredAction)
{
    global $open_ai;

    // Log the entire requiredAction for debugging (log only summary to reduce log size)
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Function handleFunctionCalls - Handling Required Action Type: " . ($requiredAction['type'] ?? 'unknown') . PHP_EOL, FILE_APPEND);

    // Start handleFunctionCalls log (log session summary only)
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Start handleFunctionCalls: Session Handled Run IDs Count: " . count($_SESSION['handled_run_ids'] ?? []) . PHP_EOL, FILE_APPEND);

    // **Begin: Check if runId has already been handled using session**
    if (!isset($_SESSION['handled_run_ids'])) {
        $_SESSION['handled_run_ids'] = []; // Initialize session variable to track runIds
    }

    // Check if the current runId has already been handled
    if (in_array($runId, $_SESSION['handled_run_ids'])) {
        // Log and exit the function to prevent reprocessing
        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Run ID {$runId} has already been handled. Skipping." . PHP_EOL, FILE_APPEND);
        return;
    } else {
        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Run ID {$runId} has not been handled. Continuing." . PHP_EOL, FILE_APPEND);
    }
    // **End: Check if runId has already been handled using session**

    // Initialize toolCalls array
    $toolCalls = [];

    // Check the type of requiredAction
    if (isset($requiredAction['type']) && $requiredAction['type'] === 'submit_tool_outputs') {
        // Extract tool_calls from submit_tool_outputs
        if (isset($requiredAction['submit_tool_outputs']['tool_calls']) && !empty($requiredAction['submit_tool_outputs']['tool_calls'])) {
            $toolCalls = $requiredAction['submit_tool_outputs']['tool_calls'];
            file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Extracted tool_calls from submit_tool_outputs." . PHP_EOL, FILE_APPEND);
        } else {
            file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] No tool calls to handle in submit_tool_outputs." . PHP_EOL, FILE_APPEND);
            return;
        }
    } elseif (isset($requiredAction['tool_calls']) && !empty($requiredAction['tool_calls'])) {
        // Extract tool_calls from tool_calls at root level
        $toolCalls = $requiredAction['tool_calls'];
        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Extracted tool_calls from root level." . PHP_EOL, FILE_APPEND);
    } else {
        // No tool calls to handle
        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] No tool calls to handle." . PHP_EOL, FILE_APPEND);
        return;
    }

    $toolOutputs = [];

    foreach ($toolCalls as $toolCall) {
        $functionName = $toolCall['function']['name'];
        $arguments = $toolCall['function']['arguments'];

        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Function handleFunctionCalls - foreach: Tool Call ID - {$toolCall['id']}" . PHP_EOL, FILE_APPEND);

        // Decode arguments if necessary
        $arguments = json_decode($arguments, true);

        // Execute the function and get the output
        switch ($functionName) {
            case 'get_category':
                $functionOutput = get_category($arguments);
                $output = json_encode($functionOutput);
                break;

            case 'get_scheduled_meeting':
                $functionOutput = get_scheduled_meeting($arguments);
                $output = json_encode($functionOutput);
                break;

            case 'detect_user_inquiries':
                $functionOutput = detect_user_inquiries($arguments, $toolCall['id']);
                $output = json_encode($functionOutput);
                break;

            default:
                $functionOutput = ["error" => "Function {$functionName} is not defined."];
                $output = json_encode($functionOutput);
        }

        // Log function execution
        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Function Call: {$functionName}, Output: " . print_r($output, true) . PHP_EOL, FILE_APPEND);

        $toolOutputs[] = [
            'tool_call_id' => $toolCall['id'],
            'output' => $output
        ];
    }

    // Submit the tool outputs directly without redundant polling
    $outputsData = ['tool_outputs' => $toolOutputs];

    $submitResponse = $open_ai->submitToolOutputs($threadId, $runId, $outputsData);

    $submitResp = json_decode($submitResponse, true);

    // Log tool output submission response (logging only essential data)
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Tool Outputs Submission Response Status: " . ($submitResp['status'] ?? 'unknown') . PHP_EOL, FILE_APPEND);

    // Handle response
    if (isset($submitResp['status']) && $submitResp['status'] === 'completed') {
        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Tool Outputs Submitted Successfully." . PHP_EOL, FILE_APPEND);
        $_SESSION['handled_run_ids'][] = $runId; // Mark runId as handled
    } elseif (isset($submitResp['error'])) {
        file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Error Submitting Tool Outputs: " . $submitResp['error']['message'] . PHP_EOL, FILE_APPEND);
    }
}

// Function to send the user message
function sendUserMessage(&$threadId, $userMessage, $vectorStoreId)
{
    global $open_ai, $useFileSearch;

    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Use File Search: " . ($useFileSearch ? 'true' : 'false') . PHP_EOL, FILE_APPEND);
    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Vector Store ID: " . ($vectorStoreId ?? 'none') . PHP_EOL, FILE_APPEND);

    // Check if threadId is provided, else create a new thread
    if (empty($threadId)) {
        // Create a new thread with the initial message
        $data = [
            'messages' => [
                [
                    'role' => 'user',
                    'content' => $userMessage,
                    // 'file_ids' => [], // Include if necessary
                ],
            ],
        ];

        $threadResponse = $open_ai->createThread($data);
        file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Create Thread Response: " . $threadResponse . PHP_EOL, FILE_APPEND);
        $threadResp = json_decode($threadResponse, true);

        if (!isset($threadResp['id'])) {
            throw new Exception('Failed to create thread.');
        }
        $threadId = $threadResp['id'];

        // Since we've added the message during thread creation, we don't need to add it again
        return;
    } else {
        // Create a message and add it to the existing thread
        $messageData = [
            'role' => 'user',
            'content' => $userMessage,
            // 'file_ids' => [], // Include if necessary
        ];

        if ($useFileSearch) {
            if (!empty($vectorStoreId)) {
                $messageData['tools'] = ['file_search'];
                $messageData['tool_resources'] = [
                    'file_search' => [
                        'vector_store_ids' => [$vectorStoreId]
                    ]
                ];
                file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] File search requested with Vector Store ID: " . $vectorStoreId . PHP_EOL, FILE_APPEND);
            } else {
                file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Warning: Vector Store ID is empty, cannot perform file search." . PHP_EOL, FILE_APPEND);
            }
        }

        $messageResponse = $open_ai->createThreadMessage($threadId, $messageData);
        file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Create Message Response: " . $messageResponse . PHP_EOL, FILE_APPEND);
        $msgResp = json_decode($messageResponse, true);

        if (!isset($msgResp['id'])) {
            throw new Exception('Failed to create message.');
        }
        $messageId = $msgResp['id'];

        return $messageId;
    }
}

// Function to get the assistant's response
function getAssistantResponse($assistantId, $threadId)
{
    global $open_ai;

    // Start a new run
    $runData = [
        'assistant_id' => $assistantId,
    ];

    $runResponse = $open_ai->createRun($threadId, $runData);
    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Create Run Response: " . $runResponse . PHP_EOL, FILE_APPEND);
    $runResp = json_decode($runResponse, true);

    if (!isset($runResp['id'])) {
        throw new Exception('Failed to create run.');
    }
    $runId = $runResp['id'];

    // Poll for completion with a maximum number of attempts
    $maxAttempts = 15; // 15 seconds
    $attempt = 0;
    do {
        $runStatusResponse = $open_ai->retrieveRun($threadId, $runId);
        $runStatusResp = json_decode($runStatusResponse, true);

        if (!isset($runStatusResp['status'])) {
            throw new Exception('Failed to retrieve run status.');
        }

        // Normalize status to lowercase for comparison
        $status = strtolower($runStatusResp['status']);
        file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Attempt {$attempt}: Run Status - {$status}" . PHP_EOL, FILE_APPEND);

        // Log full run status response
        file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Attempt {$attempt}: Full Run Status Response: " . print_r($runStatusResp, true) . PHP_EOL, FILE_APPEND);

        // Handle required actions if any
        if (isset($runStatusResp['required_action'])) {
            // With this line:
            file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Function handleFunctionCalls - Handling Required Action Type: " . (isset($requiredAction['type']) ? $requiredAction['type'] : 'Unknown') . PHP_EOL, FILE_APPEND);
            
            handleFunctionCalls($threadId, $runId, $runStatusResp['required_action']);
        }

        sleep(1); // Wait before polling again
        $attempt++;

        // Check if maximum attempts reached
        if ($attempt >= $maxAttempts) {
            throw new Exception('Run timed out after ' . $maxAttempts . ' seconds.');
        }
    } while ($status !== 'succeeded' && $status !== 'failed' && $status !== 'completed');

    if ($status === 'failed') {
        throw new Exception('Run failed.');
    }

    // Get the latest assistant message from the thread
    $messagesResponse = $open_ai->listThreadMessages($threadId);
    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] List Messages Response: " . $messagesResponse . PHP_EOL, FILE_APPEND);
    $messagesResp = json_decode($messagesResponse, true);
    $messages = $messagesResp['data'];

    // Find the latest assistant message
    $assistantMessages = array_filter($messages, function ($msg) {
        return $msg['role'] === 'assistant';
    });

    if (empty($assistantMessages)) {
        throw new Exception('No assistant messages found.');
    }

    // **Updated Line: Use reset() to get the first element (latest message)**
    $latestAssistantMessage = reset($assistantMessages);

    // Extract text from the content array
    $assistantResponse = '';
    if (isset($latestAssistantMessage['content']) && is_array($latestAssistantMessage['content'])) {
        foreach ($latestAssistantMessage['content'] as $contentItem) {
            if ($contentItem['type'] === 'text' && isset($contentItem['text']['value'])) {
                $assistantResponse .= $contentItem['text']['value'];
            }
        }
    }

    // Log the assistant response
    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Assistant Response: " . $assistantResponse . PHP_EOL, FILE_APPEND);
    file_put_contents(__DIR__ . '/tools_log.txt', "[" . date('Y-m-d H:i:s') . "] Assistant Response: " . $assistantResponse . PHP_EOL, FILE_APPEND);

    return $assistantResponse;
}

try {
    // Initialize OpenAI client
    $open_ai = new OpenAi(OPENAI_API_KEY);
    //$open_ai->setHeader(["OpenAI-Beta" => "assistants=v2"]); // Header set in OpenAi.php

    // Get the raw POST data
    $rawData = file_get_contents('php://input');
    $data = json_decode($rawData, true);

    $useFileSearch = isset($data['tools']) && in_array('file_search', $data['tools']);

    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Use File Search (Global Scope): " . ($useFileSearch ? 'true' : 'false') . PHP_EOL, FILE_APPEND);

    // Handle new conversation reset
    if (isset($data['reset']) && $data['reset'] === true) {
        if (!isset($data['assistantName'])) {
            throw new Exception('No assistant name provided.');
        }
        $assistantName = $data['assistantName'];
        $sessionKey = 'threadId_' . $assistantName;
        unset($_SESSION[$sessionKey]);

        echo json_encode([
            'response' => 'Conversation has been reset.'
        ]);
        exit; // Terminate script after responding
    }

    if (!isset($data['message'])) {
        throw new Exception('No message provided.');
    }

    $userMessage = trim($data['message']);

    if (empty($userMessage)) {
        throw new Exception('Empty message.');
    }

    if (!isset($data['assistantName'])) {
        throw new Exception('No assistant name provided.');
    }
    $assistantName = $data['assistantName'];

    if (!isset($assistantConfig[$assistantName])) {
        throw new Exception('Invalid assistant name provided.');
    }
    $assistantId = $assistantConfig[$assistantName];

    // Initialize threadId
    $sessionKey = 'threadId_' . $assistantName;
    $threadId = isset($_SESSION[$sessionKey]) ? $_SESSION[$sessionKey] : null;

    // Fetch vectorStoreId if file search is being used
    $vectorStoreId = null;
    if ($useFileSearch) {
        //$assistantDetails = getAssistantDetails($assistantId, $open_ai);
        $vectorStoreId = getAssistantDetails($assistantId);
        file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Vector Store ID Retrieved: " . $vectorStoreId . PHP_EOL, FILE_APPEND);
    }

    // Send the user message
    sendUserMessage($threadId, $userMessage, $vectorStoreId);

    // Get the assistant's response
    $assistantResponse = getAssistantResponse($assistantId, $threadId);

    // Store threadId back to session
    $_SESSION[$sessionKey] = $threadId;

    // Log the assistant response
    file_put_contents(__DIR__ . '/api_log.txt', "[" . date('Y-m-d H:i:s') . "] Final Assistant Response Sent: " . $assistantResponse . PHP_EOL, FILE_APPEND);

    // Return the response as JSON
    echo json_encode([
        'response' => $assistantResponse
    ]);
    exit; // Terminate script after responding
} catch (Exception $e) {
    // Log the error message with a timestamp
    file_put_contents(__DIR__ . '/error_log.txt', date('[Y-m-d H:i:s] ') . "Exception: " . $e->getMessage() . PHP_EOL, FILE_APPEND);

    // Return the error message as JSON
    echo json_encode([
        'error' => $e->getMessage()
    ]);
    exit; // Terminate script after handling exception
}

// Get the output buffer contents
$output = ob_get_clean();

// If there's any unexpected output, log it and send it in the response
if (!empty($output)) {
    file_put_contents(__DIR__ . '/error_log.txt', date('[Y-m-d H:i:s] ') . "Unexpected Output: " . $output . PHP_EOL, FILE_APPEND);
    // Return the output as part of the JSON response for debugging
    echo json_encode([
        'error' => 'Unexpected output: ' . $output
    ]);
    exit; // Terminate script after handling unexpected output
}
?>