<?php
require_once 'config.php'; // Includes session_start()

// Add Use Statements for clarity (though autoloading handles it)
use QuickBooksOnline\API\DataService\DataService;
use QuickBooksOnline\API\Core\OAuth2\OAuth2LoginHelper;

// --- Retrieve messages/errors from session ---
$message = $_SESSION['message'] ?? null;
$error = $_SESSION['error'] ?? null;
unset($_SESSION['message'], $_SESSION['error']); // Clear messages after reading

// --- Check Authentication Status ---
$isAuthenticated = isset($_SESSION['accessTokenKey']) && isset($_SESSION['realmId']);
$authorizationURL = null;

// --- Prepare Auth URL if Not Authenticated ---
if (!$isAuthenticated && !isset($_GET['code']) && !isset($_GET['realmId'])) {
    try {
        // Need DataService configured minimally to get the helper
        $dataServiceConfig = DataService::Configure([
            'auth_mode' => 'oauth2',
            'ClientID' => $clientID,
            'ClientSecret' => $clientSecret,
            'RedirectURI' => $redirectURI,
            'scope' => $scope,
            'baseUrl' => $baseURL
        ]);
        $oauth2LoginHelper = $dataServiceConfig->getOAuth2LoginHelper();
        $authorizationURL = $oauth2LoginHelper->getAuthorizationCodeURL();
    } catch (Exception $e) {
        // Use the logError function if defined in config.php
        if (function_exists('logError')) {
            logError("Failed to prepare authorization URL", $e->getMessage());
        }
        $error = "Error preparing connection to QuickBooks: " . $e->getMessage();
    }
}

// --- Handle Logout Request ---
if (isset($_GET['logout'])) {
    session_unset();
    session_destroy();
    // Start a new session just to set a logout message if desired
    session_start();
    $_SESSION['message'] = "You have been disconnected.";
    header("Location: index.php"); // Redirect to clean index page
    exit;
}

?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>QuickBooks Data Manager</title>
    <style>
        /* --- Basic Styles --- */
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; margin: 20px; line-height: 1.6; background-color: #f8f9fa; color: #212529; }
        h1, h2 { color: #343a40; }
        .container { max-width: 95%; margin: 0 auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
        .message { padding: 12px 15px; margin-bottom: 15px; border-radius: 4px; border: 1px solid transparent; white-space: pre-wrap; } /* Allow line breaks in messages */
        .success { background-color: #d1e7dd; border-color: #badbcc; color: #0f5132; }
        .error { background-color: #f8d7da; border-color: #f5c2c7; color: #842029; }
        .button { display: inline-block; padding: 10px 15px; background-color: #0d6efd; color: white; text-decoration: none; border-radius: 4px; margin: 5px 5px 5px 0; border: none; cursor: pointer; transition: background-color 0.15s ease-in-out; font-size: 0.95em; }
        .button:hover { background-color: #0b5ed7; }
        .button-refresh { background-color: #198754; } /* Green for refresh */
        .button-refresh:hover { background-color: #157347; }
        .button-download { background-color: #ffc107; color: #000;} /* Yellow for download */
        .button-download:hover { background-color: #e0a800; }
        .button-disconnect { background-color: #dc3545; }
        .button-disconnect:hover { background-color: #bb2d3b; }
        #display-area { margin-top: 20px; border: 1px solid #dee2e6; padding: 15px; background-color: #fff; min-height: 100px; /* Removed white-space: pre-wrap here */ word-wrap: break-word; overflow-x: auto; border-radius: 4px; }
        #display-area h3 { margin-top: 0; color: #495057; border-bottom: 1px solid #eee; padding-bottom: 5px; margin-bottom: 10px;}
        table { border-collapse: collapse; width: 100%; margin-top: 10px; table-layout: auto; /* Allow table to expand */ }
        th, td { border: 1px solid #dee2e6; padding: 6px 8px; text-align: left; font-size: 0.85em; vertical-align: top; word-break: break-word; } /* Added word-break */
        th { background-color: #e9ecef; font-weight: 600; white-space: nowrap; }
        tr:nth-child(even) { background-color: #f8f9fa; }
        pre { background-color: #e9ecef; padding: 10px; border-radius: 4px; font-size: 0.85em; white-space: pre-wrap; /* Ensure pre tags wrap */}
        hr { border: none; border-top: 1px solid #dee2e6; margin: 20px 0; }
        .action-section div { margin-bottom: 10px; }
        /* Styles for the transaction table */
        .txn-table td, .txn-table th {
            vertical-align: top;
            white-space: normal;
        }
        .txn-table .line-summary {
            font-size: 0.9em;
            list-style: none;
            padding: 0;
            margin: 0;
        }
        .txn-table .line-summary li {
            margin-bottom: 3px;
            border-bottom: 1px dotted #eee;
            padding-bottom: 3px;
        }
         .txn-table .line-summary li:last-child {
            border-bottom: none;
            padding-bottom: 0;
         }
         .uncategorized-yes { color: red; font-weight: bold; } /* Style for uncategorized flag */
    </style>
</head>
<body>
    <div class="container">
        <h1>QuickBooks Data Manager</h1>

        <!-- Display Messages/Errors -->
        <?php if ($message): ?>
            <div class="message success"><?php echo nl2br(htmlspecialchars($message)); ?></div>
        <?php endif; ?>
        <?php if ($error): ?>
            <div class="message error"><?php echo nl2br(htmlspecialchars($error)); ?></div>
        <?php endif; ?>

        <!-- Action Buttons -->
        <?php if ($isAuthenticated): ?>
            <p>Connected to QuickBooks (Realm ID: <?php echo htmlspecialchars($_SESSION['realmId']); ?>).</p>
            <div class="action-section">
                <div>
                    <!-- Consolidated Refresh Button -->
                    <a href="download_data.php" class="button button-refresh">Refresh Base Data (Accounts & Prev Month)</a>
                     <!-- Separate Curl Download Button -->
                    <a href="download_transactions_curl.php" class="button button-download">Download/Update Transactions (2023-Now cURL)</a>
                </div>
                <hr>
                <div>
                    <p>Display Saved Data:</p>
                    <button class="button display-btn" data-type="accounts" data-period="all_time">Display Accounts</button>
                    <button class="button display-btn" data-type="expenses" data-period="prev_month">Expenses (Prev. Month)</button>
                    <button class="button display-btn" data-type="revenue" data-period="prev_month">Revenue (Prev. Month)</button>
                    <button class="button display-btn" data-type="revenue_adjustments" data-period="prev_month">Revenue Adj. (Prev. Month)</button>
                    <button class="button display-btn" data-type="new_customers" data-period="prev_month">New Customers (Prev. Month)</button>
                    <button class="button display-btn" data-type="journal_entries" data-period="prev_month">Journal Entries (Prev. Month)</button>
                    <button class="button display-btn" data-type="pnl_report" data-period="prev_month">Display P&L (Prev. Month)</button>
                    <!-- Display Button for cURL Transactions (Current Month - Table View) -->
                    <button class="button display-btn" data-type="transactions_curl" data-period="current_month">Display Transactions (Table)</button>
                     <!-- ADDED RAW DATA BUTTON -->
                    <button class="button display-btn" data-type="transactions_curl_raw" data-period="current_month">Display Transactions (Raw)</button>
                </div>
            </div>
            <hr>
            <a href="index.php?logout=1" class="button button-disconnect">Disconnect</a>

        <?php elseif ($authorizationURL): ?>
            <p>You need to connect to QuickBooks to access data.</p>
            <a href="<?php echo htmlspecialchars($authorizationURL); ?>" class="button">Connect to QuickBooks</a>
        <?php else: ?>
            <!-- Show error if auth URL couldn't be generated -->
            <div class="message error">Could not generate the QuickBooks connection link. Please check application configuration and logs (config.php).</div>
        <?php endif; ?>


        <!-- Data Display Area -->
        <div id="display-area">
            <?php if ($isAuthenticated): ?>
            Click a 'Display' button above to show the corresponding saved data file content.
            <?php endif; ?>
        </div>

    </div> <!-- /container -->

    <script>
        const displayArea = document.getElementById('display-area');
        const displayButtons = document.querySelectorAll('.display-btn');

        // --- Helper to safely get nested properties ---
        const getProp = (obj, path, defaultValue = '') => {
            // Avoid errors if obj is not an object or path is invalid
            try {
                // Use optional chaining ?. if supported, otherwise reduce
                // const value = path.split('.').reduce((o, p) => o?.[p], obj); // Needs browser support
                const value = path.split('.').reduce((o, p) => o && typeof o === 'object' ? o[p] : undefined, obj);
                return value !== undefined && value !== null ? value : defaultValue;
            } catch (e) {
                console.error("getProp Error:", e, "on", obj, "with path", path); // Log error for debugging
                return defaultValue;
            }
        };

        // --- Display Raw JSON ---
        function displayRawJson(data, container) {
             const pre = document.createElement('pre');
             pre.textContent = JSON.stringify(data, null, 2); // Pretty print JSON
             container.appendChild(pre);
        }

        // --- *** Function to Display Transactions in a Table (Includes EntityRef) *** ---
        function displayTransactionTable(data, container) {
            container.innerHTML = ''; // Clear previous content

            if (!Array.isArray(data) || data.length === 0) {
                container.textContent = 'No transaction data found or data is not an array.';
                return;
            }

            const table = document.createElement('table');
            table.classList.add('txn-table'); // Add class for styling
            const thead = table.createTHead();
            const tbody = table.createTBody();
            const headerRow = thead.insertRow();

            // --- Headers remain the same ---
            const headers = ['Type', 'Date', 'Account Name', 'Account ID', 'Uncategorized?', 'Name', 'Memo/Desc', 'Total', 'Line Detail Summary'];
            headers.forEach(headerText => {
                const th = document.createElement('th');
                th.textContent = headerText;
                headerRow.appendChild(th);
            });

            // --- Keywords for Uncategorized Accounts ---
            const uncategorizedKeywords = ["uncategorized", "ask my accountant", "suspense"]; // Add others if needed, case-insensitive check later

            // --- Populate Table Rows ---
            data.forEach(txn => {
                if (!txn || typeof txn !== 'object') return; // Skip invalid entries
                const row = tbody.insertRow();

                // --- Find Primary Account and Check for Uncategorized ---
                let primaryAccountName = 'N/A';
                let primaryAccountId = '';
                let isUncategorized = false;
                let firstDetailLineProcessed = false; // Flag to prioritize the first relevant line's account

                if (txn.Line && Array.isArray(txn.Line)) {
                    for (const line of txn.Line) {
                        if (!line || typeof line !== 'object') continue;

                        const detailType = getProp(line, 'DetailType');
                        let currentAccountName = '';
                        let currentAccountId = '';

                        // Extract account based on detail type
                        if (detailType === 'AccountBasedExpenseLineDetail') {
                            currentAccountName = getProp(line, 'AccountBasedExpenseLineDetail.AccountRef.name');
                            currentAccountId = getProp(line, 'AccountBasedExpenseLineDetail.AccountRef.value');
                        } else if (detailType === 'ItemBasedExpenseLineDetail') {
                             currentAccountName = getProp(line, 'ItemBasedExpenseLineDetail.AccountRef.name'); // Usually Expense account
                             currentAccountId = getProp(line, 'ItemBasedExpenseLineDetail.AccountRef.value');
                        } else if (detailType === 'JournalEntryLineDetail') {
                            currentAccountName = getProp(line, 'JournalEntryLineDetail.AccountRef.name');
                            currentAccountId = getProp(line, 'JournalEntryLineDetail.AccountRef.value');
                        } else if (detailType === 'DiscountLineDetail') {
                            currentAccountName = getProp(line, 'DiscountLineDetail.DiscountAccountRef.name', 'Discount');
                            currentAccountId = getProp(line, 'DiscountLineDetail.DiscountAccountRef.value');
                        }
                        // Note: SalesItemLineDetail often doesn't have the *Income* account directly.

                        // Assign primary account from the first relevant line found
                        if (!firstDetailLineProcessed && currentAccountName && currentAccountName !== 'N/A') {
                            primaryAccountName = currentAccountName;
                            primaryAccountId = currentAccountId;
                            firstDetailLineProcessed = true; // Stop overwriting after finding the first
                        }

                        // Check if this line's account is uncategorized (case-insensitive)
                        if (currentAccountName) {
                             const lowerAccountName = currentAccountName.toLowerCase();
                             if (uncategorizedKeywords.some(keyword => lowerAccountName.includes(keyword))) {
                                 isUncategorized = true;
                             }
                        }
                    } // End for loop through lines
                } // End if txn.Line

                // --- Fill Table Cells ---

                // 1. Type
                row.insertCell().textContent = getProp(txn, '_EntityType', 'N/A');

                // 2. Date
                let dateStr = getProp(txn, 'TxnDate', ''); try { const d = new Date(dateStr); if (!isNaN(d.getTime())) { dateStr = d.toLocaleDateString(); } else { dateStr = getProp(txn, 'TxnDate', ''); } } catch(e) {dateStr = getProp(txn, 'TxnDate', '');} row.insertCell().textContent = dateStr;

                // 3. Account Name (Primary from lines)
                row.insertCell().textContent = primaryAccountName;

                // 4. Account ID (Primary from lines)
                row.insertCell().textContent = primaryAccountId || 'N/A';

                // 5. Uncategorized?
                const uncategorizedCell = row.insertCell();
                uncategorizedCell.textContent = isUncategorized ? 'Yes' : 'No';
                if (isUncategorized) { uncategorizedCell.classList.add('uncategorized-yes'); }

                // 6. Name (Customer/Vendor)
                let name = getProp(txn, 'CustomerRef.name') || getProp(txn, 'VendorRef.name'); row.insertCell().textContent = name || 'N/A';

                // 7. Memo/Desc (Transaction Level)
                let memo = getProp(txn, 'Memo', getProp(txn, 'PrivateNote', '')); if (memo.length > 50) memo = memo.substring(0, 50) + '...'; row.insertCell().textContent = memo;

                // 8. Total Amount
                let totalAmt = getProp(txn, 'TotalAmt', 0); try { totalAmt = parseFloat(totalAmt).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } catch(e) { totalAmt = 'N/A'; } row.insertCell().textContent = totalAmt;

                // 9. Line Detail Summary (Includes EntityRef)
                const lineCell = row.insertCell();
                const lineSummaryList = document.createElement('ul'); lineSummaryList.classList.add('line-summary'); lineCell.appendChild(lineSummaryList);

                if (txn.Line && Array.isArray(txn.Line)) {
                    txn.Line.forEach(line => {
                        if (!line || typeof line !== 'object') return;

                        const li = document.createElement('li');
                        let detailText = '';
                        let lineAmt = getProp(line, 'Amount', 0);
                        //let isCredit = false; // Only used for visual styling if desired

                        const detailType = getProp(line, 'DetailType');
                        let accountName = 'N/A';
                        let accountId = '';
                        let description = getProp(line, 'Description', '');
                        if (description.length > 40) description = description.substring(0, 40) + '...';
                        let entityText = ''; // <-- Variable to hold EntityRef info

                        // --- Get Account & Entity Info ---
                        switch(detailType) {
                            case 'SalesItemLineDetail':
                                accountName = `Item: ${getProp(line, 'SalesItemLineDetail.ItemRef.name', 'N/A')}`;
                                accountId = getProp(line, 'SalesItemLineDetail.ItemAccountRef.value', '');
                                break;
                            case 'AccountBasedExpenseLineDetail':
                                accountName = getProp(line, 'AccountBasedExpenseLineDetail.AccountRef.name', 'N/A');
                                accountId = getProp(line, 'AccountBasedExpenseLineDetail.AccountRef.value');
                                break;
                            case 'ItemBasedExpenseLineDetail':
                                const itemName = getProp(line, 'ItemBasedExpenseLineDetail.ItemRef.name');
                                accountName = getProp(line, 'ItemBasedExpenseLineDetail.AccountRef.name', 'N/A');
                                accountId = getProp(line, 'ItemBasedExpenseLineDetail.AccountRef.value');
                                if (itemName) accountName = `Item: ${itemName} (${accountName})`;
                                break;
                            case 'JournalEntryLineDetail':
                                accountName = getProp(line, 'JournalEntryLineDetail.AccountRef.name', 'N/A');
                                accountId = getProp(line, 'JournalEntryLineDetail.AccountRef.value');
                                const postingType = getProp(line, 'JournalEntryLineDetail.PostingType', '');
                                detailText += `(${postingType}) `;
                                // *** GET EntityRef for Journal Entries ***
                                const entityRef = getProp(line, 'JournalEntryLineDetail.Entity.EntityRef');
                                if (entityRef && typeof entityRef === 'object') { // Check if entityRef is an object
                                    entityText = ` [Entity: ${getProp(entityRef, 'name', 'N/A')} (${getProp(entityRef, 'value', 'N/A')})]`;
                                }
                                // *** END EntityRef Check ***
                                //if (postingType.toLowerCase() === 'credit') isCredit = true;
                                break;
                            case 'DiscountLineDetail':
                                accountName = getProp(line, 'DiscountLineDetail.DiscountAccountRef.name', 'Discount');
                                accountId = getProp(line, 'DiscountLineDetail.DiscountAccountRef.value');
                                break;
                            case 'PaymentLineDetail':
                                accountName = "Payment Application";
                                let linkedPmt = getProp(line, 'PaymentLineDetail.LinkedTxn.0');
                                if(linkedPmt) { description += ` (Links to ${getProp(linkedPmt, 'TxnType')} ${getProp(linkedPmt, 'TxnId')})`; }
                                break;
                            default:
                                accountName = detailType || 'Misc Line';
                        }

                        // Format Amount
                         try { lineAmt = parseFloat(lineAmt).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } catch(e) { lineAmt = 'N/A'; }

                        // Construct display text, including EntityRef if found
                        detailText += `${accountName}`;
                        if (accountId) detailText += ` (${accountId})`;
                        detailText += `: ${lineAmt}`;
                        if (description) { detailText += ` - "${description}"`; }
                        detailText += entityText; // *** APPEND Entity Text ***
                        li.textContent = detailText;
                        lineSummaryList.appendChild(li);
                    });
                } else if (txn._EntityType === 'Payment' && txn.LinkedTxn && Array.isArray(txn.LinkedTxn)) {
                     // Show linked txns for Payments if no standard 'Line' array exists
                     txn.LinkedTxn.forEach(linked => {
                          const li = document.createElement('li');
                          li.textContent = `Links to ${getProp(linked, 'TxnType')} ${getProp(linked, 'TxnId')}`;
                          lineSummaryList.appendChild(li);
                     });
                } else {
                    lineCell.textContent = '(No Line Details Found)';
                }
            }); // End forEach transaction

             container.appendChild(table);
        } // End displayTransactionTable


        // --- Generic Display Function (renders table or JSON) ---
        function displayData(data, container, dataType) {
             container.innerHTML = ''; // Clear

             // --- Route to specific renderer or fallback ---
             if (dataType.includes('_raw')) { // Check for raw FIRST
                 displayRawJson(data, container);
             }
             else if (dataType.includes('transactions_curl') || dataType.includes('transactions')) {
                 displayTransactionTable(data, container); // Use specific table for transactions
             }
             else if (Array.isArray(data) && data.length > 0) { // Generic table for other arrays
                 const table = document.createElement('table');
                 const thead = table.createTHead();
                 const tbody = table.createTBody();
                 const headerRow = thead.insertRow();
                 let headers = [];
                 for(let i = 0; i < Math.min(data.length, 5); i++){ if(data[i] && typeof data[i] === 'object') headers = [...new Set([...headers, ...Object.keys(data[i])])]; }
                 if(headers.length === 0 && data.length > 0 && data[0] && typeof data[0] === 'object') { headers = Object.keys(data[0]); }
                 headers.sort();
                 headers.forEach(headerText => { const th = document.createElement('th'); th.textContent = headerText; headerRow.appendChild(th); });
                 data.forEach(item => {
                    if(!item || typeof item !== 'object') return;
                    const row = tbody.insertRow();
                    headers.forEach(header => {
                        const cell = row.insertCell(); let value = item[header];
                        if (typeof value === 'object' && value !== null) { try { value = JSON.stringify(value); } catch (e) { value = '[Object]'; } }
                        if (String(value).length > 150) value = String(value).substring(0, 150) + '...';
                        cell.textContent = value !== null && value !== undefined ? value : '';
                    });
                 });
                 container.appendChild(table);
             }
             else if (typeof data === 'object' && data !== null) { // Raw JSON for objects/empty arrays
                 displayRawJson(data, container);
             } else {
                container.textContent = 'No data found or data is not in a displayable format.';
             }
        }


        // --- Function to fetch and display data based on button attributes ---
        function fetchAndDisplay(dataType, period) {
            // Construct the user-facing display type string
            let displayTypeStr = dataType.replace(/_/g, ' ');
            if (dataType.includes('_raw')) {
                displayTypeStr = displayTypeStr.replace(' raw', '') + ' (Raw JSON)';
            }
            displayArea.innerHTML = `Loading ${displayTypeStr} data (${period.replace('_', ' ')})...`;

             let filename = '';
             let dateRef = new Date(); // Today's date reference
             // Determine the target date based on 'period'
             if (period === 'prev_month') {
                 dateRef = new Date(dateRef.getFullYear(), dateRef.getMonth(), 0); // Sets day to last day of previous month
             } else if (period === 'current_month') {
                 // Use current month dateRef (already set)
             } else if (period === 'all_time') {
                 // Mainly for 'accounts' file, date doesn't matter for filename
             } else {
                 console.error("Unknown period:", period);
                 displayArea.innerHTML = `<div class="message error">Error: Unknown period specified ('${period}').</div>`;
                 return;
             }

             const year = dateRef.getFullYear();
             const month = (dateRef.getMonth() + 1).toString().padStart(2, '0'); // Month is 1-based

             // Determine the base filename by removing '_raw'
             let baseDataType = dataType.replace('_raw', '');

             if (baseDataType === 'accounts') {
                 filename = 'accounts.json';
             } else {
                 // Example: 'expenses_2024-03.json', 'transactions_curl_2024-04.json' etc.
                 filename = `${baseDataType}_${year}-${month}.json`;
             }

             // Fetch data from the backend script that reads the file
             fetch(`get_data.php?file=${encodeURIComponent(filename)}`)
                .then(response => {
                    if (!response.ok) {
                         return response.json().catch(() => response.text())
                           .then(errorData => {
                               let errorMsg = `HTTP error ${response.status}`;
                               if(typeof errorData === 'string'){ errorMsg = errorData || errorMsg; }
                               else if (errorData && errorData.error) { errorMsg = errorData.error; }
                               throw new Error(errorMsg);
                           });
                    }
                    return response.json(); // Parse JSON body on success
                })
                .then(data => {
                    // Display Title and Data
                    const title = document.createElement('h3');
                    title.textContent = `Displaying: ${filename} ${dataType.includes('_raw') ? '(Raw JSON)' : ''}`; // Update title if raw
                    displayArea.innerHTML = '';
                    displayArea.appendChild(title);
                    // Pass original dataType (including '_raw') to displayData for routing
                    displayData(data, displayArea, dataType);
                })
                .catch(error => {
                    // Display Fetch/Processing Errors
                    console.error(`Error fetching or displaying ${dataType} data ('${filename}'):`, error);
                    displayArea.innerHTML = `<div class="message error">Error loading ${dataType.replace(/_/g, ' ')} data: ${error.message}<br>(Tried to load file: ${filename}).<br>Please ensure data has been refreshed/downloaded and the file exists.</div>`;
                });
        }

        // --- Attach Event Listeners ---
        if (displayButtons.length > 0) {
            displayButtons.forEach(button => {
                button.addEventListener('click', (event) => {
                    const buttonElement = event.currentTarget;
                    const dataType = buttonElement.getAttribute('data-type');
                    const period = buttonElement.getAttribute('data-period') || 'current_month';
                    if (!dataType) {
                        console.error("Button is missing data-type attribute:", buttonElement);
                        return;
                    }
                    fetchAndDisplay(dataType, period);
                });
            });
        }

    </script>

</body>
</html>