<?php

namespace App\Modules\Invoices\controllers\api\v1;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use App\Modules\Stock\Stock;
use App\Modules\Bookings\Task;
use App\Modules\Invoices\InvoiceItem;
use App\Modules\Invoices\InvoiceTransaction;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\ExternalApiSafetyService;
use App\Seraph\Services\GlobalMailerService;
use App\Modules\Invoices\InvoiceEmail;

abstract class AbstractInvoicesControllerREST extends BaseController
{
    protected $model = 'App\Modules\Invoices\Invoice';

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index(Request $request)
    {
        $classname = $this->model;
        
        // Optimize for table view - only select necessary fields
        $query = $classname::query()->select([
            'id',
            'xeroId', 
            'type',
            'task_id',
            'client_id',
            'client_type',
            'total_price',
            'created_by',
            'created_at',
            'paid',
            'xero_sync_status',
            'invoice_date'
        ]);
        
        // Optimize relationships - only load necessary fields
        $query->with([
            'creator:id,first_name,last_name', // Only name fields for "Created By"
            'items:invoice_id,quantity,price_sold_exc', // Only for calculating totals, not full details
        ]);

        if ($request->input('date')) {
            $query = $query->where('created_at', 'like', $request->input('date', '0000-00-00 00:00:00').'%');
        }
        if ($request->input('start_date')) {
            $query = $query->where('created_at', '>=', $request->input('start_date'));
        }
        if ($request->input('end_date')) {
            $query = $query->where('created_at', '<=', $request->input('end_date'));
        }
        if ($request->input('type')) {
            $query->where('type', $request->input('type'));
        }
        if ($request->input('paid') && $request->input('paid') != 'false') {
            $query->where('paid', 1);
        }
        if ($request->input('due') && $request->input('due') != 'false') {
            $query->where('paid', 0);
        }
        if ($request->input('not_emailed') && $request->input('not_emailed') != 'false') {
            $query->has('emails', '=', 0);
        }
        if ($request->input('emailed') && $request->input('emailed') != 'false') {
            $query->has('emails', '>', 0);
        }
        // Search by invoice number
        if ($request->input('invoice_number')) {
            $query->where('invoice_number', 'like', '%'.$request->input('invoice_number').'%');
        }
        if ($request->input('client_id') && $request->input('client_type')) {
            $query->where('client_id', $request->input('client_id'))->where('client_type', $request->input('client_type'));
        }
        
        // Search by Seraph ID (invoice ID)
        if ($request->input('id')) {
            $query->where('id', $request->input('id'));
        }
        
        // Search by customer name
        if ($request->input('customer_name')) {
            $query->whereHas('client', function($q) use ($request) {
                $q->where('name', 'like', '%' . $request->input('customer_name') . '%');
            });
        }

        // Load client for table view (polymorphic relationship - can't optimize field selection safely)
        if ($request->input('client')) {
            $query->with('client'); // Load full client due to polymorphic nature
        }
        
        // Add email count for "Emailed" column
        $query->withCount('emails as emailed_count');

        // Apply limits and execute the query
        $query = $query->orderBy('id', 'desc')->paginate($request->input('limit', 20));

        $response = [
            'success'      => true,
            'data'         => $query->getCollection()->toArray(),
            'total'        => $query->total(),
            'current_page' => $query->currentPage(),
            'last_page'    => $query->lastPage(),
            'from'         => $query->firstItem(),
            'to'           => $query->lastItem(),
        ];

        return response()->json($response);
    }


    private function processXeroSync($invoice){
        $xeroService = app(\App\Modules\Xero\Services\XeroService::class);
        return $xeroService->sendInvoiceToXero($invoice);
    }



    /**
     * Validates that all items have sufficient stock available
     *
     * @param Request $request
     * @return array Array of validation errors, empty if all items are valid
     */
    protected function validateStockAvailability(Request $request)
    {
        $errors = [];
        $items = $request->input('items', []);
        $taskId = $request->input('task_id');
        
        foreach ($items as $index => $item) {
            // Skip items without stock_id or with ignore_stock enabled
            if (!isset($item['stock_id']) || !$item['stock_id'] || (isset($item['ignore_stock']) && $item['ignore_stock'])) {
                continue;
            }
            
            $stock = Stock::find($item['stock_id']);
            if ($stock && !$stock->unlimited) {
                $requiredQuantity = $item['quantity'] ?? 0;
                
                // Calculate available stock considering allocations
                $availableQuantity = $stock->quantity_available();
                
                // If this invoice is for a specific task, add back any stock allocated to this task
                // since that allocated stock should be available for the task's invoice
                if ($taskId) {
                    $allocatedToThisTask = \App\Modules\Bookings\Part::where('related_type', 'task')
                        ->where('related_id', $taskId)
                        ->where('stock_id', $stock->id)
                        ->sum('quantity');
                    
                    $availableQuantity += $allocatedToThisTask;
                }
                
                if ($availableQuantity < $requiredQuantity) {
                    $errors[] = "Insufficient stock for '{$item['name']}'. Available: {$availableQuantity}, Required: {$requiredQuantity}";
                }
            }
        }
        
        return $errors;
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        $startTime = microtime(true);
        $lastTime = $startTime;

        $logTiming = function($step) use (&$lastTime) {
            $now = microtime(true);
            $sinceLast = round(($now - $lastTime) * 1000, 2);
            $lastTime = $now;
            
            Log::info("Timing: $step", [
                'time' => now()->format('Y-m-d H:i:s.u'),
                'ms_since_last' => $sinceLast
            ]);
        };

        Log::info('Starting invoice creation process', [
            'time' => now()->format('Y-m-d H:i:s.u'),
            'request_data' => [
                'client_id' => $request->input('client_id'),
                'client_type' => $request->input('client_type'),
                'items_count' => count($request->input('items', [])),
                'total_price' => $request->input('total_price'),
                'memory_usage' => memory_get_usage(true) / 1024 / 1024 . 'MB'
            ]
        ]);

        $logTiming('After initial log');

        // Filter out empty items before processing
        $originalItemsCount = count($request->input('items', []));
        $items = collect($request->input('items', []))
            ->filter(function ($item) {
                return !empty($item['name']) && isset($item['quantity']) && $item['quantity'] > 0;
            })
            ->values()
            ->all();
        
        Log::info('Items filtering complete', [
            'time' => now()->format('Y-m-d H:i:s.u'),
            'original_items_count' => $originalItemsCount,
            'filtered_items_count' => count($items),
            'items_removed' => $originalItemsCount - count($items)
        ]);
        
        $logTiming('After items filtering');

        // Validate stock availability before attempting to save
        $stockErrors = $this->validateStockAvailability($request);
        
        $logTiming('After stock validation');

        if (!empty($stockErrors)) {
            $message = count($stockErrors) === 1 
                ? $stockErrors[0] 
                : 'Multiple items have insufficient stock. Please check quantities or use "Ignore Stock" option.';
                
            return response()->json([
                'error' => $message
            ], 422);
        }

        try {
            $invoice = '';
            $classname = $this->model;

            $logTiming('Before DB transaction');

            try {
                DB::transaction(function () use (&$invoice, $request, $items, $classname, $logTiming) {
                    // Calculate total price before creating invoice
                    $totalPrice = collect($items)->sum(function($item) {
                        return ($item['price_sold_exc'] ?? 0) * ($item['quantity'] ?? 1);
                    });

                    if (!$request->input('include_vat', true)) {
                        $totalPrice *= ($request->input('vat', 20) / 100 + 1);
                    }

                    $logTiming('After total calculation');

                    // Debug invoice date processing
                    $rawInvoiceDate = $request->input('invoice_date');
                    
                    // More robust date processing
                    if (empty($rawInvoiceDate)) {
                        $processedInvoiceDate = now()->format('Y-m-d');
                    } else {
                        $timestamp = strtotime($rawInvoiceDate);
                        if ($timestamp === false || $timestamp < 0) {
                            // If strtotime fails, use current date
                            $processedInvoiceDate = now()->format('Y-m-d');
                        } else {
                            $processedInvoiceDate = date('Y-m-d', $timestamp);
                        }
                    }
                    
                    Log::info('Invoice date processing', [
                        'raw_invoice_date' => $rawInvoiceDate,
                        'processed_invoice_date' => $processedInvoiceDate,
                        'strtotime_result' => strtotime($rawInvoiceDate ?? 'now'),
                        'current_time' => time()
                    ]);

                    // Create invoice with initial data including total price
                    $invoice = $classname::create([
                        'client_id'    => $request->input('client_id'),
                        'client_type'  => $request->input('client_type'),
                        'vat'          => $request->input('vat', 20),
                        'include_vat'  => $request->input('include_vat', true),
                        'total_price'  => round($totalPrice, 2),
                        'discount'     => $request->input('discount', 0),
                        'task_id'      => $request->input('task_id', null),
                        'project_id'   => $request->input('project_id', null),
                        'paid'         => 0,
                        'invoice_date' => $processedInvoiceDate,
                    ]);

                    $logTiming('After invoice creation');

                    // Handle method of payment for sales
                    $method_of_payment = $request->input('method_of_payment');
                    if ($invoice->type == 'sale' && empty($method_of_payment)) {
                        throw new \Exception('Please select a method of payment.');
                    }

                    // Handle task/project relationships
                    if ($invoice->task) {
                        $invoice->task->invoice_number = $invoice->id;
                        if (!$invoice->task->save()) {
                            throw new \Exception($invoice->task->errors->first());
                        }
                    }

                    if ($invoice->project) {
                        $invoice->project->invoice_number = $invoice->id;
                        if (!$invoice->project->save()) {
                            throw new \Exception($invoice->project->errors->first());
                        }
                    }

                    if ($request->input('booking_fee')) {
                        Task::where('id', $request->input('booking_fee'))->update(['booking_fee_id' => $invoice->id]);
                    }

                    // Bulk insert all items at once
                    Log::info('About to process items for insertion', [
                        'time' => now()->format('Y-m-d H:i:s.u'),
                        'invoice_id' => $invoice->id,
                        'items_empty_check' => empty($items),
                        'items_count' => count($items),
                        'items_data' => $items
                    ]);

                    if (!empty($items)) {
                        $itemsToInsert = collect($items)->map(function($data) use ($invoice) {
                            return [
                                'invoice_id'    => $invoice->id,
                                'name'          => $data['name'],
                                'stock_id'      => $data['stock_id'] ?? null,
                                'type'          => $data['type'],
                                'quantity'      => $data['quantity'],
                                'price_sold_exc'=> $data['price_sold_exc'],
                                'discount'      => 0,
                                'ignore_stock'  => $data['ignore_stock'] ?? 0,

                                'created_at'    => now(),
                                'updated_at'    => now(),
                                'created_by'    => auth()->id(),
                                'updated_by'    => auth()->id()
                            ];
                        })->all();

                        Log::info('Items prepared for insertion', [
                            'time' => now()->format('Y-m-d H:i:s.u'),
                            'invoice_id' => $invoice->id,
                            'items_to_insert_count' => count($itemsToInsert),
                            'items_to_insert_data' => $itemsToInsert
                        ]);

                        InvoiceItem::insert($itemsToInsert);

                        Log::info('Invoice items saved', [
                            'time' => now()->format('Y-m-d H:i:s.u'),
                            'invoice_id' => $invoice->id,
                            'items_count' => count($itemsToInsert)
                        ]);
                    } else {
                        Log::warning('No items to insert - items array is empty', [
                            'time' => now()->format('Y-m-d H:i:s.u'),
                            'invoice_id' => $invoice->id,
                            'items_data' => $items
                        ]);
                    }

                    // Save a transaction if needed
                    $this->storeTransaction($invoice, $request);

                    Log::info('Database transaction completed', [
                        'time' => now()->format('Y-m-d H:i:s.u'),
                        'invoice_id' => $invoice->id
                    ]);
                });
            } catch (\Exception $e) {
                Log::error('Database transaction failed', [
                    'time' => now()->format('Y-m-d H:i:s.u'),
                    'error' => $e->getMessage(),
                    'error_code' => $e->getCode(),
                    'error_file' => $e->getFile(),
                    'error_line' => $e->getLine(),
                    'trace' => $e->getTraceAsString()
                ]);
                throw $e; // Re-throw the exception to be caught by the outer try-catch
            }

            $logTiming('After DB transaction');

            // Verification: Check if items were actually saved to database
            $savedItemsCount = InvoiceItem::where('invoice_id', $invoice->id)->count();
            Log::info('Post-transaction verification', [
                'time' => now()->format('Y-m-d H:i:s.u'),
                'invoice_id' => $invoice->id,
                'items_count_in_db' => $savedItemsCount,
                'expected_items_count' => count($items)
            ]);

            // Step 2: Load invoice with relationships for Xero
            $invoice = $classname::with(['items', 'client'])
                ->where('id', $invoice->id)
                ->first();

            if (!$invoice) {
                throw new \Exception("Failed to reload invoice after creation");
            }

            // Ensure total_price is preserved correctly
            // The issue is that the total_price might be 0 when reloaded from DB
            // So we calculate it from the items if it's 0
            if ($invoice->total_price == 0) {
                $calculatedTotal = $invoice->items->sum(function($item) {
                    return $item->price_sold_exc * $item->quantity;
                });
                
                if ($calculatedTotal > 0) {
                    Log::warning('Invoice total_price was 0, recalculating from items', [
                        'invoice_id' => $invoice->id,
                        'original_total_price' => $invoice->total_price,
                        'calculated_total_price' => $calculatedTotal
                    ]);
                    
                    // Update the invoice with the correct total_price
                    $invoice->total_price = $calculatedTotal;
                    $invoice->save();
                }
            }

            $logTiming('After invoice reload');

            // Log the exact data being sent to Xero
            Log::info('Data being sent to Xero', [
                'invoice_id' => $invoice->id,
                'total_price' => $invoice->total_price,
                'items_total' => $invoice->items->sum(function($item) {
                    return $item->price_sold_exc * $item->quantity;
                }),
                'client_xero_id' => $invoice->client->xeroId ?? null
            ]);

            $logTiming('After DB transaction');

            $endTime = microtime(true);
            Log::info('Invoice creation process completed', [
                'time' => now()->format('Y-m-d H:i:s.u'),
                'invoice_id' => $invoice->id,
                'total_duration_ms' => round(($endTime - $startTime) * 1000, 2)
            ]);

            // Prepare initial response data
            $responseData = [
                'id' => $invoice->id,
                'type' => $invoice->type,
                'invoice_number' => $invoice->invoice_number,
                'total_price' => $invoice->total_price,
                'amount_due' => $invoice->amount_due,
                'invoice_date' => $invoice->invoice_date,
                'client_id' => $invoice->client_id,
                'client_type' => $invoice->client_type,
                'method_of_payment' => $invoice->method_of_payment,
                'xeroId' => $invoice->xeroId ?? null,
                'xero_syncing' => false,
                'xero_sync_error' => null,
                'success' => true,
                'message' => 'Invoice created successfully',
                'print_url' => url('generate-invoice/' . ($invoice->invoice_number ?? $invoice->id))
            ];

            // Step 3: Try Xero sync AFTER invoice is created (only for sales, not invoices)
            if ($invoice->type === 'sale') {
                Log::info('Processing Xero sync for sale', [
                    'invoice_id' => $invoice->id,
                    'type' => $invoice->type
                ]);
                
                // Check if Xero integration is enabled
                $xeroEnabled = \App\Models\Setting::get('integrations.xero.enabled', false);
                
                if (!$xeroEnabled) {
                    Log::info('Xero integration is disabled, skipping sync', [
                        'invoice_id' => $invoice->id,
                        'type' => $invoice->type
                    ]);
                    $responseData['notifications'] = [[
                        'type' => 'info',
                        'message' => 'Invoice created successfully. Xero integration is disabled.'
                    ]];
                } else {
                    $retryCount = 0;
                    $maxRetries = 3;
                    $xeroSyncSuccess = false;
                    $xeroSyncError = null;
                    
                    while ($retryCount < $maxRetries) {
                        try {
                            $this->processXeroSync($invoice);
                            $xeroSyncSuccess = true;
                            Log::info('Xero sync completed successfully', [
                                'invoice_id' => $invoice->id,
                                'attempt' => $retryCount + 1
                            ]);
                            break;
                        } catch (\Exception $e) {
                            $retryCount++;
                            $xeroSyncError = $e->getMessage();
                            
                            Log::warning("Xero sync attempt $retryCount failed", [
                                'invoice_id' => $invoice->id,
                                'error' => $e->getMessage(),
                                'attempt' => $retryCount,
                                'max_retries' => $maxRetries
                            ]);
                            
                            if ($retryCount >= $maxRetries) {
                                Log::error("Xero sync failed after {$maxRetries} attempts - invoice created but not synced to Xero", [
                                    'invoice_id' => $invoice->id,
                                    'final_error' => $e->getMessage()
                                ]);
                                break;
                            }
                            
                            // Wait before retry (exponential backoff)
                            usleep(($retryCount * 500000)); // 0.5s, 1s, 1.5s
                        }
                    }
                    
                    // Add notification based on Xero sync result
                    if ($xeroSyncSuccess) {
                        $responseData['notifications'] = [[
                            'type' => 'success',
                            'message' => 'Invoice created and successfully synced to Xero.'
                        ]];
                    } else {
                        $responseData['notifications'] = [[
                            'type' => 'warning',
                            'message' => 'Invoice created successfully, but could not be synced to Xero. ' . 
                                        ($xeroSyncError ? 'Error: ' . $xeroSyncError : 'Please check your Xero integration settings.')
                        ]];
                        $responseData['xero_sync_error'] = $xeroSyncError;
                    }
                }
            } else {
                Log::info('Skipping Xero sync for invoice (not a sale)', [
                    'invoice_id' => $invoice->id,
                    'type' => $invoice->type
                ]);
            }

            return response()->json($responseData);

        } catch (Exception $e) {
            Log::error('Failed to create invoice', [
                'time' => now()->format('Y-m-d H:i:s.u'),
                'error' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'error_file' => $e->getFile(),
                'error_line' => $e->getLine(),
                'trace' => $e->getTraceAsString()
            ]);
            
            // Return a proper JSON response with appropriate status code
            $statusCode = 400;
            $errorMessage = $e->getMessage();
            
            // Handle specific validation errors
            if (strpos($errorMessage, 'Please select a method of payment') !== false) {
                $statusCode = 422;
                return response()->json([
                    'error' => $errorMessage,
                    'field' => 'method_of_payment'
                ], $statusCode);
            }
            
            // Handle other validation errors
            if (strpos($errorMessage, 'Please') !== false || strpos($errorMessage, 'required') !== false) {
                $statusCode = 422;
            }
            
            return response()->json([
                'error' => $errorMessage
            ], $statusCode);
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function show(Request $request, $id)
    {
        $classname = $this->model;
        $sale = $classname::query()->findOrFail($id);

        // Set invoice_number to the invoice ID if it's not set
        if (empty($sale->invoice_number)) {
            $sale->invoice_number = $sale->id;
        }

        // Load all relationships needed for detail view
        $relationships = ['items', 'items.stock', 'creator', 'emails.creator', 'transactions.creator'];
        
        if ($request->input('client')) {
            $relationships[] = 'client'; // Full client details for detail view
        }
        
        $sale->load($relationships);
        return $sale;
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function update(Request $request, $id)
    {
        // Validate stock availability before attempting to update
        $stockErrors = $this->validateStockAvailability($request);
        if (!empty($stockErrors)) {
            // Return a single clear message instead of multiple fields
            $message = count($stockErrors) === 1 
                ? $stockErrors[0] 
                : 'Multiple items have insufficient stock. Please check quantities or use "Ignore Stock" option.';
                
            return response()->json([
                'error' => $message
            ], 422);
        }

        try {
            $classname = $this->model;
            $sale = $classname::query()->findOrFail($id);

            DB::transaction((function () use (&$sale, $request) {

                // Calculate the amount paid so far against this sale receipt
                $total_paid = InvoiceTransaction::query()->where('invoice_id', $sale->id)->sum('amount');

                $sale->fill($request->all());
                
                // Debug invoice date processing in update
                $rawInvoiceDate = $request->input('invoice_date');
                
                // More robust date processing
                if (empty($rawInvoiceDate)) {
                    $processedInvoiceDate = now()->format('Y-m-d');
                } else {
                    $timestamp = strtotime($rawInvoiceDate);
                    if ($timestamp === false || $timestamp < 0) {
                        // If strtotime fails, use current date
                        $processedInvoiceDate = now()->format('Y-m-d');
                    } else {
                        $processedInvoiceDate = date('Y-m-d', $timestamp);
                    }
                }
                
                Log::info('Update - Invoice date processing', [
                    'invoice_id' => $sale->id,
                    'raw_invoice_date' => $rawInvoiceDate,
                    'processed_invoice_date' => $processedInvoiceDate,
                    'strtotime_result' => strtotime($rawInvoiceDate ?? 'now'),
                    'current_time' => time()
                ]);
                
                $sale->invoice_date = $processedInvoiceDate;
                if ($sale->amount_due == 0) {
                    $sale->paid = 1;
                }

                if (! $sale->save()) {
                    // Failed to save the sale
                    throw new Exception($sale->errors->first(), 1);
                }

                if ($sale->task) {
                    $sale->task->invoice_number = $sale->id;
                    if (! $sale->task->save()) {
                        throw new \Exception($sale->task->errors->first());
                    }
                }

                // Delete all sale items
                $sale->items()->delete();

                // Filter out empty items before processing (same as in store method)
                $originalItemsCount = count($request->input('items', []));
                $items = collect($request->input('items', []))
                    ->filter(function ($item) {
                        return !empty($item['name']) && isset($item['quantity']) && $item['quantity'] > 0;
                    })
                    ->values()
                    ->all();
                
                Log::info('Update method - Items filtering complete', [
                    'time' => now()->format('Y-m-d H:i:s.u'),
                    'original_items_count' => $originalItemsCount,
                    'filtered_items_count' => count($items),
                    'items_removed' => $originalItemsCount - count($items),
                    'invoice_id' => $sale->id
                ]);

                foreach ($items as $data) {
                    if (isset($data['stock_id'])) {
                        $stock = Stock::withTrashed()->find($data['stock_id']);
                        if (! $stock || $stock->name != $data['name']) {
                            $data['stock_id'] = null;
                        }
                    }

                    $product = new InvoiceItem([
                        'name'           => $data['name'],
                        'stock_id'       => isset($data['stock_id']) ? $data['stock_id'] : null,
                        'type'           => $data['type'],
                        'quantity'       => $data['quantity'],
                        'price_sold_exc' => $data['price_sold_exc'],
                        'discount'       => 0,
                        'ignore_stock'   => isset($data['ignore_stock']) ? $data['ignore_stock'] : 0,


                    ]);

                    $product = $sale->items()->save($product);
                }

                // Set invoice number if not already set
                if (empty($sale->invoice_number)) {
                    $sale->invoice_number = 'INV-' . $sale->id;
                }

                // Recalculate total price based on saved items
                $totalPrice = collect($items)->sum(function($item) {
                    return ($item['price_sold_exc'] ?? 0) * ($item['quantity'] ?? 1);
                });

                if (!$request->input('include_vat', true)) {
                    $totalPrice *= ($request->input('vat', 20) / 100 + 1);
                }

                $sale->total_price = round($totalPrice, 2);

                Log::info('Update method - Total price recalculated', [
                    'time' => now()->format('Y-m-d H:i:s.u'),
                    'invoice_id' => $sale->id,
                    'new_total_price' => $sale->total_price,
                    'items_count' => count($items)
                ]);

                // Save a transaction with it if it's a sales receipt
                if ($sale->type == 'sale') {
                    $this->storeTransaction($sale, $request);
                    $sale->load('transactions');
                }

                // Save the invoice to trigger last events (update total & paid status)
                $sale->save();
            }));
        } catch (Exception $e) {
            return response()->json(['error' => $e->getMessage()], 400);
        }

        if ($request->input('print')) {
            // Set print URL to our new PDF generation endpoint
            $sale->print_url = url('generate-invoice/' . ($sale->invoice_number ?? $sale->id));
        }

        return $sale;
    }
    
    /**
     * Send an invoice to Xero
     *
     * @param Request $request
     * @param int $id
     * @return Response
     */
    public function sendToXero(Request $request, $id)
    {
        try {
            $xeroService = app(\App\Modules\Xero\Services\XeroService::class);
            
            // Load the invoice with relationships
            $invoice = $this->loadInvoiceById($id);
            
            $result = $xeroService->sendInvoiceToXero($invoice);
            
            if ($result['already_synced'] ?? false) {
                return response()->json([
                    'success' => true,
                    'message' => 'Invoice already synced to Xero',
                    'xero_invoice_id' => $result['xero_invoice_id'],
                    'invoice' => $invoice->fresh() // Return updated invoice object
                ]);
            }
            
            // Return the result with the updated invoice object
            $result['invoice'] = $invoice->fresh();
            return response()->json($result);
            
        } catch (\Exception $e) {
            Log::error('Failed to send invoice to Xero', [
                'invoice_id' => $id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'Failed to send invoice to Xero: ' . $e->getMessage()
            ], 400);
        }
    }
    
    /**
     * Load invoice by ID based on controller model
     */
    protected function loadInvoiceById($id)
    {
        $modelClass = $this->model;
        $invoice = $modelClass::with(['client', 'items'])->find($id);
        
        if (!$invoice) {
            throw new Exception("Invoice not found: {$id}");
        }
        
        return $invoice;
    }
    

    
    // Note: Xero integration now uses direct API through Admin → Integration Settings
    // Old microservice integration has been removed
    
    /**
     * Store transaction for sales receipts
     */
    protected function storeTransaction($invoice, $request)
    {
        if ($invoice->type == 'sale') {
            // Add debugging logs
            Log::info('storeTransaction called', [
                'invoice_id' => $invoice->id,
                'invoice_total_price' => $invoice->total_price,
                'invoice_total_price_type' => gettype($invoice->total_price),
                'invoice_attributes' => $invoice->getAttributes(),
                'invoice_original' => $invoice->getOriginal(),
                'invoice_raw_total_price' => $invoice->getRawOriginal('total_price'),
                'method_of_payment' => $request->input('method_of_payment', 'cash'),
                'invoice_exists' => $invoice->exists,
                'invoice_was_recently_created' => $invoice->wasRecentlyCreated
            ]);

            // Ensure we have the correct total_price
            $amount = $invoice->total_price;
            if ($amount == 0) {
                // If total_price is 0, calculate from items
                $amount = $invoice->items->sum(function($item) {
                    return $item->price_sold_exc * $item->quantity;
                });
                
                Log::warning('Invoice total_price was 0, using calculated amount', [
                    'invoice_id' => $invoice->id,
                    'calculated_amount' => $amount
                ]);
            }

            $invoice->transactions()->delete();
            $transaction = InvoiceTransaction::create([
                'invoice_id'        => $invoice->id,
                'amount'            => $amount,
                'method_of_payment' => $request->input('method_of_payment', 'cash'),
                'payment_date'      => date('Y-m-d H:i:s'),
                'notes'             => 'Payment for a sales receipt from Seraph3. Paid '.$request->input('method_of_payment', 'cash'),
            ]);

            Log::info('Transaction created', [
                'transaction_id' => $transaction->id,
                'transaction_amount' => $transaction->amount,
                'transaction_attributes' => $transaction->getAttributes(),
                'amount_passed_to_create' => $amount
            ]);

            if (! $transaction->id) {
                throw new Exception('Failed to store the transaction for this sales receipt');
            }
        }
    }

    /**
     * Email an invoice with PDF attachment using generate-invoice endpoint
     *
     * @param Request $request
     * @return Response
     */
    public function emailInvoice(Request $request)
    {
        try {
            $invoiceId = $request->input('invoice_id');
            $invoiceNumber = $request->input('invoice_number');
            $sendTo = $request->input('send_to');
            $subject = $request->input('subject');
            $message = $request->input('message');

            if (!$sendTo || !$subject || !$message) {
                return response()->json(['error' => 'Missing required fields'], 400);
            }

            // Find the invoice
            $classname = $this->model;
            $invoice = null;
            
            if ($invoiceId) {
                $invoice = $classname::find($invoiceId);
            } elseif ($invoiceNumber) {
                $invoice = $classname::where('invoice_number', $invoiceNumber)->first();
            }

            if (!$invoice) {
                return response()->json(['error' => 'Invoice not found'], 404);
            }

            // Generate PDF directly using the PDF service
            $tempPdfPath = storage_path('app/temp/invoice_' . ($invoice->invoice_number ?? $invoice->id) . '.pdf');
            
            // Ensure temp directory exists
            $tempDir = dirname($tempPdfPath);
            if (!file_exists($tempDir)) {
                mkdir($tempDir, 0755, true);
            }

            try {
                // Load relationships for PDF generation
                $invoice->load('items', 'client');
                
                // Load sites only for clients (not customers)
                if ($invoice->client && $invoice->client_type === 'client') {
                    $invoice->client->load('sites');
                }
                
                // Get company settings
                $companySettings = [
                    'name' => 'Wiseserve Ltd (Computer Assistance)',
                    'address' => '123 Example Street',
                    'city' => 'Oxford',
                    'postcode' => 'OX1 2AB',
                    'phone' => env('COMPANY_PHONE', ''),
                    'email' => env('COMPANY_EMAIL', ''),
                    'website' => env('COMPANY_WEBSITE', '')
                ];
                
                // Calculate totals
                $subtotal = $invoice->items->sum(function($item) {
                    return $item->quantity * $item->price_sold_exc;
                });
                $vatAmount = $invoice->include_vat ? 
                    ($subtotal / (100 / $invoice->vat + 1)) : 
                    ($subtotal * $invoice->vat / 100);
                $finalTotal = $invoice->include_vat ? $subtotal : ($subtotal + $vatAmount);
                
                // Prepare data for PDF
                $data = [
                    'invoice' => $invoice,
                    'company' => $companySettings,
                    'client' => $invoice->client,
                    'items' => $invoice->items,
                    'subtotal' => $subtotal,
                    'vatAmount' => $vatAmount,
                    'finalTotal' => $finalTotal,
                    'accountCode' => 'INV-' . $invoice->id,
                ];
                
                // Generate PDF using Laravel PDF
                $pdf = \PDF::loadView('pdf.invoice-simple', $data);
                $pdf->setPaper('A4', 'portrait');
                
                // Get raw PDF content
                $pdfContent = $pdf->output();
                
                if (!$pdfContent || strlen($pdfContent) === 0) {
                    return response()->json(['error' => 'Failed to generate PDF - empty content'], 500);
                }
                
                // Save PDF content to temporary file
                if (file_put_contents($tempPdfPath, $pdfContent) === false) {
                    return response()->json(['error' => 'Failed to save PDF to temporary file'], 500);
                }
                
            } catch (\Exception $e) {
                Log::error('Failed to generate PDF for email: ' . $e->getMessage(), [
                    'invoice_id' => $invoice->id,
                    'error' => $e->getMessage(),
                    'trace' => $e->getTraceAsString()
                ]);
                return response()->json(['error' => 'Failed to generate PDF: ' . $e->getMessage()], 500);
            }

            // Send email using GlobalMailerService with CC/BCC enabled
            $emailSent = GlobalMailerService::send(
                'emails.invoice',
                ['content' => $message],
                function ($mail) use ($subject, $sendTo, $tempPdfPath) {
                    $mail->to($sendTo)
                         ->subject($subject)
                         ->attach($tempPdfPath);
                },
                null, // from_email (use default from SMTP settings)
                null, // from_name (use default from SMTP settings)
                false  // use_cc_bcc = false to include CC/BCC from global SMTP settings
            );

            // Clean up temporary file
            if (file_exists($tempPdfPath)) {
                unlink($tempPdfPath);
            }

            if (!$emailSent) {
                return response()->json(['error' => 'Failed to send email'], 500);
            }

            // Record the email in database
            InvoiceEmail::create([
                'invoice_id' => $invoice->id,
                'sent_to'    => $sendTo,
                'subject'    => $subject,
                'message'    => $message,
                'created_by' => auth()->id(),
            ]);

            return response()->json(['success' => true, 'message' => 'Email sent successfully']);

        } catch (\Exception $e) {
            Log::error('Failed to email invoice: ' . $e->getMessage(), [
                'invoice_id' => $request->input('invoice_id'),
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            
            // Clean up temporary file in case of error
            if (isset($tempPdfPath) && file_exists($tempPdfPath)) {
                unlink($tempPdfPath);
            }
            
            return response()->json(['error' => 'Failed to send email: ' . $e->getMessage()], 500);
        }
    }
}
