<?php

namespace App\Modules\Xero\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
use Exception;
use App\Modules\Xero\Exceptions\XeroApiException;

class XeroService implements XeroServiceInterface
{
    private const HTTP_POST = 'POST';
    private const HTTP_PUT = 'PUT';
    private const LOG_PREFIX = '[XERO]';
    private const DEFAULT_INVOICE_TYPE = 'ACCREC';

    protected $authService;

    public function __construct(XeroAuthService $authService)
    {
        $this->authService = $authService;
    }

    protected function makeApiRequest($method, $endpoint, $data = null)
    {
        $url = rtrim($this->authService->getBaseUrl(), '/') . '/' . ltrim($endpoint, '/');
        
        try {
            $token = $this->authService->getValidToken();
        } catch (Exception $e) {
            $this->logError("Xero authentication failed", ['error' => $e->getMessage()]);
            throw new Exception("Xero authentication failed: " . $e->getMessage());
        }

        $this->logInfo("API Request: {$method} {$endpoint}", [
            'tenant_id' => $token->tenant_id,
            'tenant_name' => $token->tenant_name ?? 'N/A'
        ]);

        // Implement retry logic for transient errors
        $response = retry(3, function () use ($method, $url, $data) {
            $response = $this->authService->httpClient()->$method($url, $data);
            
            // If it's a transient error, throw to trigger retry
            if ($response->status() === 429 || $response->status() >= 500) {
                throw new Exception("Transient error: {$response->status()}");
            }
            
            return $response;
        }, 1000); // 1 second delay between retries

        if (!$response->successful()) {
            $this->logError("Xero API Error: {$endpoint}", [
                'status' => $response->status(),
                'body'   => $response->body(),
                'tenant_id' => $token->tenant_id
            ]);
            
            // Handle authentication errors specifically
            if ($response->status() === 401) {
                $this->logError("Xero authentication expired, token may need refresh", [
                    'tenant_id' => $token->tenant_id
                ]);
                $this->handleTokenInvalidation();
            }
            
            throw new XeroApiException(
                "Xero API request failed: {$response->status()} - {$response->body()}",
                $response->status(),
                $response->body(),
                $endpoint
            );
        }

        return $response->json();
    }

    public function sendInvoiceToXero($invoice): array
    {
        $this->logInfo('sendInvoiceToXero', [
            'invoice_id' => $invoice->id ?? 'N/A',
            'invoice_number' => $invoice->invoice_number ?? 'N/A'
        ]);

        if (!$invoice || !is_object($invoice)) {
            throw new Exception("Invalid invoice object");
        }

        $isNew = empty($invoice->xeroId);
        $xeroInvoice = $isNew ? $this->createInvoice($invoice) : $this->updateInvoice($invoice);

        if (empty($xeroInvoice['InvoiceID'])) {
            throw new Exception('Failed to sync invoice in Xero');
        }

        $this->syncInvoiceMetadata($invoice, $xeroInvoice, $isNew);

        $this->logInfo('Invoice synced to Xero successfully', [
            'invoice_id' => $invoice->id,
            'xero_id' => $xeroInvoice['InvoiceID'],
            'action' => $isNew ? 'created' : 'updated'
        ]);

        return $this->response(true, 'Invoice synced', $xeroInvoice['InvoiceID'], $invoice);
    }

    private function syncInvoiceMetadata($invoice, $xeroInvoice, $isNew)
    {
        $hasChanges = false;

        if ($isNew) {
            $invoice->xeroId = $xeroInvoice['InvoiceID'];
            $hasChanges = true;
        }

        $newSyncTime = now();
        if ($invoice->xero_synced_at != $newSyncTime || $hasChanges) {
            $invoice->xero_synced_at = $newSyncTime;
            $hasChanges = true;
        }

        if ($hasChanges) {
            $invoice->save();
            $this->logInfo('Invoice database record updated', [
                'invoice_id' => $invoice->id,
                'changes' => ['xero_id' => $isNew, 'sync_timestamp' => true]
            ]);
        } else {
            $this->logInfo('No database changes needed for invoice', [
                'invoice_id' => $invoice->id
            ]);
        }
    }

    public function sendInvoiceToXeroById($invoiceId, $type = null): array
    {
        $invoice = match ($type) {
            'sale'    => \App\Modules\Invoices\Sale::with(['client', 'items'])->find($invoiceId),
            'invoice' => \App\Modules\Invoices\Invoice::with(['client', 'items'])->find($invoiceId),
            default   => \App\Modules\Invoices\Invoice::with(['client', 'items'])->find($invoiceId)
                        ?? \App\Modules\Invoices\Sale::with(['client', 'items'])->find($invoiceId)
        };

        if (!$invoice) {
            throw new Exception("Invoice not found: {$invoiceId}");
        }

        return $this->sendInvoiceToXero($invoice);
    }

    private function sendInvoiceToXeroApi($invoice, string $method = self::HTTP_POST)
    {
        $action = $method === self::HTTP_PUT ? 'update' : 'create';
        
        $this->logInfo("Sending invoice to Xero API", [
            'invoice_id' => $invoice->id,
            'method' => $method,
            'action' => $action
        ]);

        if ($method === self::HTTP_PUT && !empty($invoice->xeroId)) {
            // For updates, use the new approach: POST with invoice ID in URL
            $this->logInfo("Using new update approach with POST and invoice ID in URL", [
                'invoice_id' => $invoice->id,
                'xero_id' => $invoice->xeroId
            ]);
            
            // Get the existing invoice from Xero first
            $xeroInvoice = $this->makeApiRequest('GET', '/Invoices/' . $invoice->xeroId);
            $currentInvoice = $xeroInvoice['Invoices'][0] ?? null;
            
            if (!$currentInvoice) {
                throw new Exception("Invoice not found in Xero: {$invoice->xeroId}");
            }
            
            // Build update payload using existing data
            $updateData = $this->buildUpdatePayload($currentInvoice, $invoice);
            
            // Use POST with invoice ID in URL
            $response = $this->makeApiRequest('POST', '/Invoices/' . $invoice->xeroId, $updateData);
            $result = $response['Invoices'][0] ?? $response;
        } else {
            // For creates, use the old approach
            $invoiceData = $this->buildInvoiceData($invoice);
            $data = ['Invoices' => [$invoiceData]];
            $response = $this->makeApiRequest($method, '/Invoices', $data);
            $result = $response['Invoices'][0] ?? null;
        }

        if ($result) {
            $this->logInfo("Invoice {$action}d successfully in Xero", [
                'invoice_id' => $invoice->id,
                'xero_id' => $result['InvoiceID'],
                'action' => $action
            ]);
        }

        // Handle payment if needed
        if ($result && $invoice->isPaid()) {
            $this->handlePaymentForPaidInvoice($invoice, $result['InvoiceID']);
        }

        return $result;
    }

    private function createInvoice($invoice)
    {
        return $this->sendInvoiceToXeroApi($invoice, self::HTTP_POST);
    }

    private function updateInvoice($invoice)
    {
        return $this->sendInvoiceToXeroApi($invoice, self::HTTP_PUT);
    }

    /**
     * Common function to handle payment creation for paid invoices
     */
    private function handlePaymentForPaidInvoice($invoice, $xeroInvoiceId)
    {
        $this->logInfo('Skipping payment sync - all invoices kept as DRAFT', [
            'invoice_id' => $invoice->id,
            'xero_invoice_id' => $xeroInvoiceId
        ]);
        
        // Skip payment creation for DRAFT invoices
        return;

        try {
            $transaction = $this->getLatestTransaction($invoice);

            if ($this->shouldSkipPaymentSync($transaction)) {
                $this->logInfo('Skipping payment sync - already synced or no transaction found', [
                    'invoice_id' => $invoice->id,
                    'transaction_id' => $transaction->id ?? null,
                    'xero_payment_id' => $transaction->xeroId ?? null
                ]);
                return;
            }

            $payment = $this->createAndStorePayment($invoice, $transaction, $xeroInvoiceId);

            if (!empty($payment['PaymentID'])) {
                $this->savePaymentReference($transaction, $payment['PaymentID']);
                $this->logInfo('Payment created and saved in Xero', [
                    'invoice_id' => $invoice->id,
                    'transaction_id' => $transaction->id,
                    'xero_payment_id' => $payment['PaymentID']
                ]);
            }

        } catch (Exception $e) {
            $this->logError('Failed to sync payment for paid invoice', [
                'invoice_id' => $invoice->id,
                'error' => $e->getMessage()
            ]);
        }
    }

    private function getLatestTransaction($invoice)
    {
        return $invoice->transactions()->latest()->first();
    }

    private function shouldSkipPaymentSync($transaction): bool
    {
        if (empty($transaction) || !empty($transaction->xeroId)) {
            return true;
        }
        
        // Check if the invoice already has payments in Xero
        $invoice = $transaction->invoice;
        if ($invoice && !empty($invoice->xeroId)) {
            try {
                $xeroInvoice = $this->makeApiRequest('GET', '/Invoices/' . $invoice->xeroId);
                $invoiceData = $xeroInvoice['Invoices'][0] ?? null;
                
                if ($invoiceData && !empty($invoiceData['AmountPaid']) && $invoiceData['AmountPaid'] > 0) {
                    $this->logInfo('Skipping payment sync - invoice already has payments in Xero', [
                        'invoice_id' => $invoice->id,
                        'xero_id' => $invoice->xeroId,
                        'amount_paid' => $invoiceData['AmountPaid']
                    ]);
                    return true;
                }
            } catch (Exception $e) {
                $this->logError('Failed to check Xero invoice payment status', [
                    'invoice_id' => $invoice->id,
                    'error' => $e->getMessage()
                ]);
            }
        }
        
        return false;
    }

    private function createAndStorePayment($invoice, $transaction = null, $xeroInvoiceId = null)
    {
        $this->logInfo('Creating Xero payment record', [
            'invoice_id' => $invoice->id,
            'transaction_id' => $transaction->id ?? null,
            'amount' => $transaction->amount ?? $invoice->total_price,
            'method' => $transaction->method_of_payment ?? null,
            'xero_invoice_id' => $xeroInvoiceId
        ]);

        return $this->createPaymentForInvoice($invoice, $transaction, $xeroInvoiceId);
    }

    private function savePaymentReference($transaction, $paymentId)
    {
        if ($transaction) {
            $transaction->xeroId = $paymentId;
            $transaction->save();
        }
    }

    private function buildInvoiceData($invoice)
    {
        // Validate line items before proceeding
        $lineItems = $this->buildLineItems($invoice->items);
        
        if (empty($lineItems)) {
            throw new XeroApiException(
                "Invoice must have at least one line item",
                400,
                null,
                '/Invoices'
            );
        }

        // Determine status based on invoice state and Xero ID
        $status = $this->determineInvoiceStatus($invoice);

        $invoiceData = [
            'Type'             => config('xero.invoice.default_type', self::DEFAULT_INVOICE_TYPE),
            'Contact'          => ['Name' => $invoice->client->name ?? 'Walk in Customer'],
            'LineItems'        => $lineItems,
            // 'InvoiceNumber' => null, // Let Xero auto-assign invoice numbers
            'Date'             => $invoice->invoice_date ? Carbon::parse($invoice->invoice_date)->format('Y-m-d') : now()->format('Y-m-d'),
            'DueDate'          => Carbon::parse($invoice->invoice_date ?? now())->addDays(config('xero.invoice.due_date_days'))->format('Y-m-d'),
            'Status'           => $status,
            'LineAmountTypes'  => $invoice->include_vat ? 'Inclusive' : 'Exclusive',
            'Reference'        => (string)$invoice->id, // Send just the ID without INV- prefix
            'CurrencyCode'     => config('xero.invoice.default_currency'),
        ];

        $this->logInfo('Built invoice data', [
            'invoice_id' => $invoice->id,
            'invoice_number' => 'AUTO-ASSIGNED', // Xero will auto-assign
            'status' => $invoiceData['Status'],
            'type' => $invoiceData['Type'],
            'line_items_count' => count($invoiceData['LineItems']),
            'contact_name' => $invoiceData['Contact']['Name'],
            'reference' => $invoiceData['Reference'],
            'date' => $invoiceData['Date'],
            'due_date' => $invoiceData['DueDate']
        ]);

        return $invoiceData;
    }

    /**
     * Determine the appropriate status for an invoice
     */
    private function determineInvoiceStatus($invoice)
    {
        // Always create invoices as DRAFT regardless of payment status
        return 'DRAFT';
    }

    private function buildLineItems($items)
    {
        if (empty($items)) {
            return [];
        }

        $originalCount = count($items);
        
        $validItems = collect($items)->filter(function ($item) {
            // Filter out items with invalid data
            return !empty($item->name) && 
                   isset($item->quantity) && 
                   isset($item->price_sold_exc) &&
                   $item->quantity > 0 && 
                   $item->price_sold_exc >= 0;
        });

        $filteredCount = $originalCount - $validItems->count();
        
        if ($filteredCount > 0) {
            $this->logInfo('Filtered out invalid line items', [
                'original_count' => $originalCount,
                'valid_count' => $validItems->count(),
                'filtered_count' => $filteredCount
            ]);
        }

        return $validItems->map(function ($item) {
            return [
                'Description' => $item->name ?? 'Invoice Item',
                'Quantity'    => $item->quantity ?? 1,
                'UnitAmount'  => round($item->price_sold_exc ?? 0, 2),
                'AccountCode' => config('xero.invoice.default_account_code'),
                'TaxType'     => config('xero.invoice.default_tax_type'),
            ];
        })->values()->toArray();
    }

    private function createPaymentForInvoice($invoice, $transaction = null, $xeroInvoiceId = null)
    {
        $data = ['Payments' => [$this->buildPaymentData($invoice, $transaction, $xeroInvoiceId)]];
        return $this->makeApiRequest('POST', '/Payments', $data)['Payments'][0] ?? null;
    }

    private function buildPaymentData($invoice, $transaction = null, $xeroInvoiceId = null)
    {
        $reference = config('xero.payment.reference_prefix') . $invoice->id;
        
        // Add payment method to reference if available
        if ($transaction && $transaction->method_of_payment) {
            $suffix = config('xero.payment.method_suffix', ' - {method}');
            $reference .= str_replace('{method}', $transaction->method_of_payment, $suffix);
        }
        
        // Use the passed xeroInvoiceId if available, otherwise fall back to invoice->xeroId
        $invoiceId = $xeroInvoiceId ?? $invoice->xeroId;
        
        return [
            'Invoice'   => ['InvoiceID' => $invoiceId],
            'Account'   => ['AccountID' => $this->getBankAccountId()],
            'Amount'    => $transaction ? $transaction->amount : $invoice->total_price,
            'Date'      => $invoice->invoice_date ? Carbon::parse($invoice->invoice_date)->format('Y-m-d') : now()->format('Y-m-d'),
            'Reference' => $reference,
        ];
    }

    private function getBankAccountId()
    {
        $settings = \App\Models\Setting::get('integrations.xero') ?? [];

        if (!empty($settings['bank_account_id'])) {
            // Validate cached account is still active
            try {
                $accounts = $this->makeApiRequest('GET', '/Accounts')['Accounts'] ?? [];
                $cachedAccount = collect($accounts)->firstWhere('AccountID', $settings['bank_account_id']);
                
                if ($cachedAccount && $cachedAccount['Status'] === 'ACTIVE' && $cachedAccount['Type'] === 'BANK') {
                    return $settings['bank_account_id'];
                }
                
                // Cached account is no longer valid, clear it and find a new one
                $this->logInfo('Cached bank account is no longer active, finding new account', [
                    'cached_account_id' => $settings['bank_account_id'],
                    'cached_status' => $cachedAccount['Status'] ?? 'NOT_FOUND'
                ]);
            } catch (Exception $e) {
                $this->logError('Failed to validate cached bank account', ['error' => $e->getMessage()]);
            }
        }

        try {
            $accounts = $this->makeApiRequest('GET', '/Accounts')['Accounts'] ?? [];

            $bankAccount = collect($accounts)
                ->where('Type', 'BANK')
                ->where('Status', 'ACTIVE')
                ->first();

            if (!$bankAccount) {
                throw new XeroApiException(
                    'No active bank account found in Xero. Please ensure you have at least one active bank account configured in your Xero organization.',
                    404,
                    null,
                    '/Accounts'
                );
            }

            $settings['bank_account_id'] = $bankAccount['AccountID'];
            \App\Models\Setting::set('integrations.xero', $settings);

            $this->logInfo('Bank account ID cached successfully', [
                'account_id' => $bankAccount['AccountID'],
                'account_name' => $bankAccount['Name'] ?? 'Unknown'
            ]);

            return $bankAccount['AccountID'];
        } catch (XeroApiException $e) {
            $this->logError('Failed to retrieve bank account from Xero', [
                'error' => $e->getMessage(),
                'status_code' => $e->getStatusCode()
            ]);
            throw $e;
        } catch (Exception $e) {
            $this->logError('Unexpected error while retrieving bank account', [
                'error' => $e->getMessage()
            ]);
            throw new XeroApiException(
                'Failed to retrieve bank account information from Xero',
                500,
                null,
                '/Accounts',
                $e
            );
        }
    }

    public function getItems(): array
    {
        return $this->safeApiCall('/Items', 'Items');
    }

    public function getTypes(): array
    {
        return $this->safeApiCall('/Accounts', 'Accounts');
    }

    /**
     * Get organization details from Xero
     */
    public function getOrganization(): array
    {
        try {
            $response = $this->makeApiRequest('GET', '/Organisation');
            
            if (empty($response['Organisations'])) {
                throw new Exception('No organization data returned from Xero API');
            }
            
            return $response['Organisations'][0];
            
        } catch (Exception $e) {
            $this->logError('Failed to get organization details', ['error' => $e->getMessage()]);
            throw new Exception('Failed to get organization details: ' . $e->getMessage());
        }
    }

    /**
     * Sync bank accounts from Xero
     */
    public function syncBankAccounts(): array
    {
        try {
            $response = $this->makeApiRequest('GET', '/Accounts');
            
            if (empty($response['Accounts'])) {
                return [];
            }
            
            // Filter for bank accounts only
            $bankAccounts = array_filter($response['Accounts'], function($account) {
                return $account['Type'] === 'BANK';
            });
            
            $this->logInfo('Synced bank accounts from Xero', [
                'total_accounts' => count($response['Accounts']),
                'bank_accounts' => count($bankAccounts)
            ]);
            
            return array_values($bankAccounts);
            
        } catch (Exception $e) {
            $this->logError('Failed to sync bank accounts', ['error' => $e->getMessage()]);
            throw new Exception('Failed to sync bank accounts: ' . $e->getMessage());
        }
    }

    /**
     * Safe API call wrapper
     */
    private function safeApiCall($endpoint, $dataKey): array
    {
        try {
            $response = $this->makeApiRequest('GET', $endpoint);
            return $response[$dataKey] ?? [];
        } catch (Exception $e) {
            $this->logError("Failed to fetch {$dataKey} from Xero", ['error' => $e->getMessage()]);
            return [];
        }
    }

    /**
     * Test Xero connection and validate token
     */
    public function testConnection(): bool
    {
        try {
            // Try to get a valid token
            $token = $this->authService->getValidToken();
            
            if (!$token) {
                $this->logError("No valid token found for connection test");
                return false;
            }

            // Test API call to get organization details
            $response = $this->makeApiRequest('GET', '/Organisation');
            
            if (empty($response['Organisations'])) {
                $this->logError("No organization data returned from Xero API");
                return false;
            }

            $org = $response['Organisations'][0];
            $this->logInfo("Connection test successful", [
                'organization' => $org['Name'] ?? 'Unknown',
                'tenant_id' => $token->tenant_id
            ]);

            return true;
            
        } catch (Exception $e) {
            $this->logError("Connection test failed", ['error' => $e->getMessage()]);
            return false;
        }
    }

    /**
     * Validate current Xero token status
     */
    public function validateToken(): array
    {
        try {
            $token = $this->authService->getValidToken();
            
            if (!$token) {
                return [
                    'valid' => false,
                    'message' => 'No valid token found',
                    'needs_oauth' => true
                ];
            }

            // Check if token is expired
            if ($token->isExpired()) {
                return [
                    'valid' => false,
                    'message' => 'Token is expired',
                    'needs_refresh' => true,
                    'expires_at' => $token->expires_at
                ];
            }

            // Test API call
            $response = $this->makeApiRequest('GET', '/Organisation');
            
            if (empty($response['Organisations'])) {
                return [
                    'valid' => false,
                    'message' => 'API call failed - no organization data',
                    'needs_oauth' => true
                ];
            }

            $org = $response['Organisations'][0];
            
            return [
                'valid' => true,
                'message' => 'Token is valid',
                'organization' => $org['Name'] ?? 'Unknown',
                'tenant_id' => $token->tenant_id,
                'expires_at' => $token->expires_at,
                'minutes_until_expiry' => $token->minutesUntilExpiry()
            ];
            
        } catch (Exception $e) {
            return [
                'valid' => false,
                'message' => 'Token validation failed: ' . $e->getMessage(),
                'needs_oauth' => true
            ];
        }
    }

    /**
     * Validate current Xero token status
     */
    public function syncPayment($transaction): array
    {
        $this->logInfo("Starting payment sync", [
            'transaction_id' => $transaction->id,
            'invoice_id' => $transaction->invoice_id,
            'amount' => $transaction->amount,
            'method_of_payment' => $transaction->method_of_payment
        ]);

        $invoice = $transaction->invoice;

        if (!$invoice) {
            throw new Exception("Invoice not found for transaction");
        }

        // Check if we should skip payment sync
        if ($this->shouldSkipPaymentSync($transaction)) {
            $this->logInfo("Skipping payment sync - already synced or invoice has payments", [
                'transaction_id' => $transaction->id,
                'invoice_id' => $invoice->id
            ]);
            return [
                'success' => true,
                'message' => 'Payment sync skipped - already exists',
                'xero_payment_id' => $transaction->xeroId ?? null
            ];
        }

        // Ensure invoice exists in Xero and is in correct status for payment
        if (empty($invoice->xeroId)) {
            $this->logInfo("Invoice not synced to Xero, creating invoice first", [
                'invoice_id' => $invoice->id
            ]);
            $this->sendInvoiceToXero($invoice);
        } else {
            $this->logInfo("Invoice already exists in Xero, checking status for payment", [
                'invoice_id' => $invoice->id,
                'xero_id' => $invoice->xeroId
            ]);
            
            // Keep invoices as DRAFT - do not auto-authorize paid invoices
            // if ($invoice->isPaid()) {
            //     $this->ensureInvoiceAuthorized($invoice);
            // }
        }

        // Skip payment creation for DRAFT invoices (Xero requirement)
        $this->logInfo("Skipping payment creation - invoices are kept as DRAFT", [
            'invoice_id' => $invoice->id,
            'amount' => $transaction->amount
        ]);

        // Payments cannot be created for DRAFT invoices in Xero
        // $payment = $this->createPaymentForInvoice($invoice, $transaction);
        // $transaction->xeroId = $payment['PaymentID'] ?? null;
        // $transaction->save();

        $this->logInfo("Payment sync skipped - DRAFT invoice", [
            'transaction_id' => $transaction->id,
            'xero_payment_id' => null
        ]);

        return [
            'success' => true,
            'message' => 'Payment synced to Xero',
            'xero_payment_id' => $payment['PaymentID'] ?? null
        ];
    }



    public function deletePayment($xeroPaymentId): bool
    {
        try {
            $this->makeApiRequest('DELETE', '/Payments/' . $xeroPaymentId);
            return true;
        } catch (Exception $e) {
            $this->logError("Delete payment failed", ['error' => $e->getMessage()]);
            throw new Exception("Delete payment failed: " . $e->getMessage());
        }
    }



    private function formatInvoiceNumber($invoice)
    {
        $format = config('xero.invoice.number_format', 'INV-{id}');
        return str_replace('{id}', $invoice->id, $format);
    }

    private function response($success, $message, $xeroInvoiceId, $invoice)
    {
        return [
            'success'         => $success,
            'message'         => $message,
            'xero_invoice_id' => $xeroInvoiceId,
            'invoice_number'  => $this->formatInvoiceNumber($invoice),
            'already_synced'  => !$success && $xeroInvoiceId
        ];
    }

    private function logInfo($message, array $context = [])
    {
        Log::info(self::LOG_PREFIX . ' ' . $message, $context);
    }

    private function logError($message, array $context = [])
    {
        Log::error(self::LOG_PREFIX . ' ' . $message, $context);
    }



    /**
     * Handle duplicate invoices in Xero by finding the correct one
     */
    private function handleDuplicateInvoices($invoice)
    {
        try {
            $reference = (string)$invoice->id;
            
            $this->logInfo('Checking for duplicate invoices in Xero', [
                'invoice_id' => $invoice->id,
                'reference' => $reference
            ]);
            
            // Search for all invoices with this reference
            $response = $this->makeApiRequest('GET', '/Invoices?where=Reference=="' . $reference . '"');
            $invoices = $response['Invoices'] ?? [];
            
            $this->logInfo('Found invoices with same reference', [
                'invoice_id' => $invoice->id,
                'reference' => $reference,
                'count' => count($invoices)
            ]);
            
            if (count($invoices) > 1) {
                $this->logInfo('Multiple invoices found with same number, cleaning up duplicates', [
                    'invoice_id' => $invoice->id,
                    'invoice_number' => $invoiceNumber,
                    'count' => count($invoices)
                ]);
                
                // Find the invoice that matches our stored Xero ID
                $correctInvoice = null;
                foreach ($invoices as $xeroInvoice) {
                    if ($xeroInvoice['InvoiceID'] === $invoice->xeroId) {
                        $correctInvoice = $xeroInvoice;
                        break;
                    }
                }
                
                if (!$correctInvoice) {
                    $this->logError('Could not find our invoice among duplicates', [
                        'invoice_id' => $invoice->id,
                        'stored_xero_id' => $invoice->xeroId,
                        'found_invoices' => array_column($invoices, 'InvoiceID')
                    ]);
                    throw new Exception("Could not find our invoice among duplicates");
                }
                
                // Delete other invoices (keep only the one that matches our stored ID)
                foreach ($invoices as $xeroInvoice) {
                    if ($xeroInvoice['InvoiceID'] !== $invoice->xeroId) {
                        $this->logInfo('Deleting duplicate invoice', [
                            'invoice_id' => $invoice->id,
                            'duplicate_xero_id' => $xeroInvoice['InvoiceID'],
                            'status' => $xeroInvoice['Status']
                        ]);
                        
                        // Delete the duplicate invoice
                        $deleteData = [
                            'Invoices' => [
                                [
                                    'InvoiceID' => $xeroInvoice['InvoiceID'],
                                    'Status' => 'DELETED'
                                ]
                            ]
                        ];
                        
                        $this->makeApiRequest('PUT', '/Invoices', $deleteData);
                    }
                }
                
                $this->logInfo('Successfully cleaned up duplicate invoices', [
                    'invoice_id' => $invoice->id,
                    'kept_xero_id' => $invoice->xeroId
                ]);
            }
            
            return true;
        } catch (Exception $e) {
            $this->logError('Failed to handle duplicate invoices', [
                'invoice_id' => $invoice->id,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * Build update payload by merging existing Xero invoice data with our changes
     */
    private function buildUpdatePayload($xeroInvoice, $invoice, $newStatus = null)
    {
        // Start with the existing invoice data from Xero - include ALL required fields
        $updateData = [
            'InvoiceID' => $xeroInvoice['InvoiceID'],
            'InvoiceNumber' => $xeroInvoice['InvoiceNumber'],
            'Type' => $xeroInvoice['Type'],
            'Contact' => $xeroInvoice['Contact'],
            'Date' => $xeroInvoice['DateString'],
            'DueDate' => $xeroInvoice['DueDateString'],
            'LineAmountTypes' => $xeroInvoice['LineAmountTypes'],
            'CurrencyCode' => $xeroInvoice['CurrencyCode'],
            'LineItems' => $xeroInvoice['LineItems'],
            'SubTotal' => $xeroInvoice['SubTotal'],
            'TotalTax' => $xeroInvoice['TotalTax'],
            'Total' => $xeroInvoice['Total'],
            'AmountDue' => $xeroInvoice['AmountDue'],
            'AmountPaid' => $xeroInvoice['AmountPaid'],
            'CurrencyRate' => $xeroInvoice['CurrencyRate'],
            'IsDiscounted' => $xeroInvoice['IsDiscounted'],
            'SentToContact' => $xeroInvoice['SentToContact'],
        ];
        
        // Add optional fields if they exist
        if (!empty($xeroInvoice['Reference'])) {
            $updateData['Reference'] = $xeroInvoice['Reference'];
        }
        
        if (!empty($xeroInvoice['BrandingThemeID'])) {
            $updateData['BrandingThemeID'] = $xeroInvoice['BrandingThemeID'];
        }
        
        if (!empty($xeroInvoice['Url'])) {
            $updateData['Url'] = $xeroInvoice['Url'];
        }
        
        // Only update status if provided and different
        if ($newStatus && $newStatus !== $xeroInvoice['Status']) {
            $updateData['Status'] = $newStatus;
        }
        
        $this->logInfo('Built update payload from existing Xero invoice', [
            'invoice_id' => $invoice->id,
            'xero_id' => $xeroInvoice['InvoiceID'],
            'invoice_number' => $xeroInvoice['InvoiceNumber'],
            'current_status' => $xeroInvoice['Status'],
            'new_status' => $newStatus,
            'line_items_count' => count($xeroInvoice['LineItems'])
        ]);
        
        return $updateData;
    }

    /**
     * Ensure invoice is AUTHORISED before payment (if it's paid)
     */
    private function ensureInvoiceAuthorized($invoice)
    {
        try {
            $this->logInfo('Starting ensureInvoiceAuthorized', [
                'invoice_id' => $invoice->id,
                'xero_id' => $invoice->xeroId,
                'invoice_number' => $this->formatInvoiceNumber($invoice)
            ]);
            
            // First, handle any duplicate invoices
            $this->handleDuplicateInvoices($invoice);
            
            // Get current invoice from Xero using the stored Xero ID
            $xeroInvoice = $this->makeApiRequest('GET', '/Invoices/' . $invoice->xeroId);
            $currentInvoice = $xeroInvoice['Invoices'][0] ?? null;
            
            if (!$currentInvoice) {
                throw new Exception("Invoice not found in Xero: {$invoice->xeroId}");
            }
            
            $currentStatus = $currentInvoice['Status'] ?? 'DRAFT';
            $xeroInvoiceId = $currentInvoice['InvoiceID'] ?? null;
            
            $this->logInfo('Retrieved invoice from Xero', [
                'invoice_id' => $invoice->id,
                'local_xero_id' => $invoice->xeroId,
                'xero_invoice_id' => $xeroInvoiceId,
                'current_status' => $currentStatus,
                'is_paid' => $invoice->isPaid(),
                'invoice_number' => $currentInvoice['InvoiceNumber'] ?? 'N/A'
            ]);
            
            // Verify the Xero invoice ID matches what we expect
            if ($xeroInvoiceId !== $invoice->xeroId) {
                $this->logError('Xero invoice ID mismatch - this suggests data corruption', [
                    'invoice_id' => $invoice->id,
                    'expected_xero_id' => $invoice->xeroId,
                    'actual_xero_id' => $xeroInvoiceId
                ]);
                throw new Exception("Xero invoice ID mismatch: expected {$invoice->xeroId}, got {$xeroInvoiceId}");
            }
            
            // Only update if invoice is paid and not already AUTHORISED
            if ($invoice->isPaid() && $currentStatus !== 'AUTHORISED') {
                $this->logInfo('Invoice is paid but not AUTHORISED, updating status', [
                    'invoice_id' => $invoice->id,
                    'xero_id' => $invoice->xeroId,
                    'current_status' => $currentStatus
                ]);
                
                // Build update payload using existing Xero invoice data
                $updatePayload = $this->buildUpdatePayload($currentInvoice, $invoice, 'AUTHORISED');
                
                $this->logInfo('Sending invoice update to Xero', [
                    'invoice_id' => $invoice->id,
                    'xero_id' => $invoice->xeroId,
                    'invoice_number' => $updatePayload['InvoiceNumber'],
                    'status' => $updatePayload['Status'],
                    'type' => $updatePayload['Type'],
                    'line_items_count' => count($updatePayload['LineItems'])
                ]);
                
                // Try using POST with the invoice ID in the URL path
                $result = $this->makeApiRequest('POST', '/Invoices/' . $invoice->xeroId, $updatePayload);
                
                $this->logInfo('Invoice status updated to AUTHORISED successfully', [
                    'invoice_id' => $invoice->id,
                    'xero_id' => $invoice->xeroId,
                    'result_invoice_id' => $result['Invoices'][0]['InvoiceID'] ?? 'N/A'
                ]);
            } else {
                $this->logInfo('Invoice already in correct status for payment', [
                    'invoice_id' => $invoice->id,
                    'status' => $currentStatus,
                    'is_paid' => $invoice->isPaid()
                ]);
            }
        } catch (Exception $e) {
            $this->logError('Failed to ensure invoice is authorized', [
                'invoice_id' => $invoice->id,
                'xero_id' => $invoice->xeroId,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }
} 