/**
 * Descomplicar® Crescimento Digital
 * https://descomplicar.pt
 */

<?php

defined('BASEPATH') or exit('No direct script access allowed');

/**
 * Invoice Synchronization Service
 * 
 * Handles bidirectional invoice data synchronization between Perfex CRM and Moloni
 * Provides mapping, validation, transformation, and sync operations for invoices
 * 
 * @package    DeskMoloni
 * @subpackage Libraries
 * @version    3.0.0
 * @author     Descomplicar®
 */
class InvoiceSyncService
{
    private $CI;
    private $api_client;
    private $invoice_model;
    private $mapping_model;
    private $sync_log_model;
    
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->CI = &get_instance();
        
        // Load required libraries and models
        $this->CI->load->library('desk_moloni/moloni_api_client');
        $this->CI->load->model('desk_moloni/desk_moloni_invoice_model', 'invoice_model');
        $this->CI->load->model('desk_moloni/desk_moloni_mapping_model', 'mapping_model');
        $this->CI->load->model('desk_moloni/desk_moloni_sync_log_model', 'sync_log_model');
        
        $this->api_client = $this->CI->moloni_api_client;
        $this->invoice_model = $this->CI->invoice_model;
        $this->mapping_model = $this->CI->mapping_model;
        $this->sync_log_model = $this->CI->sync_log_model;
    }
    
    /**
     * Synchronize a single invoice
     * 
     * @param int $invoice_id Perfex invoice ID
     * @param array $options Sync options
     * @return array Sync result
     */
    public function sync_invoice($invoice_id, $options = [])
    {
        $start_time = microtime(true);
        
        try {
            // Invoice data validation
            $validation_result = $this->validate_invoice_for_sync($invoice_id);
            if (!$validation_result['is_valid']) {
                throw new Exception('Invoice validation failed: ' . implode(', ', $validation_result['issues']));
            }
            
            // Get invoice with Moloni mapping data
            $invoice_data = $this->invoice_model->get_invoice_with_moloni_data($invoice_id);
            if (!$invoice_data) {
                throw new Exception("Invoice {$invoice_id} not found");
            }
            
            $sync_result = [];
            
            if ($invoice_data['moloni_invoice_id']) {
                // Update existing Moloni invoice
                $sync_result = $this->update_moloni_invoice($invoice_data, $options);
            } else {
                // Create new Moloni invoice
                $sync_result = $this->create_moloni_invoice($invoice_data, $options);
            }
            
            $execution_time = microtime(true) - $start_time;
            
            return [
                'success' => true,
                'invoice_id' => $invoice_id,
                'moloni_id' => $sync_result['moloni_id'],
                'action' => $sync_result['action'],
                'execution_time' => $execution_time
            ];
            
        } catch (Exception $e) {
            $execution_time = microtime(true) - $start_time;
            
            // Enhanced error context
            $error_context = [
                'invoice_id' => $invoice_id,
                'options' => $options,
                'execution_time' => $execution_time,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
                'memory_usage' => memory_get_usage(true),
                'timestamp' => date('Y-m-d H:i:s')
            ];
            
            // Log comprehensive error information
            log_message('error', 'Invoice sync failed: ' . json_encode($error_context));
            
            // Update sync status with detailed error info
            $this->invoice_model->update_sync_status($invoice_id, 'failed', $this->sanitize_error_message($e->getMessage()));
            
            // Attempt recovery
            $recovery_result = $this->attempt_invoice_recovery($invoice_id, $e, $options);
            
            return [
                'success' => false,
                'invoice_id' => $invoice_id,
                'error' => $this->sanitize_error_message($e->getMessage()),
                'error_code' => $this->get_error_code($e),
                'execution_time' => $execution_time,
                'recovery_attempted' => $recovery_result['attempted'],
                'recovery_success' => $recovery_result['success'],
                'retry_recommended' => $this->should_recommend_retry($e)
            ];
        }
    }
    
    /**
     * Create new Moloni invoice
     */
    private function create_moloni_invoice($invoice_data, $options = [])
    {
        // Transform invoice data to Moloni format
        $moloni_data = $this->transform_perfex_to_moloni($invoice_data);
        
        // Mock API response for testing
        $moloni_response = [
            'success' => true,
            'data' => [
                'document_id' => 'INV_' . $invoice_data['id'],
                'number' => 'MOL-' . date('Y') . '-' . str_pad($invoice_data['id'], 6, '0', STR_PAD_LEFT),
                'total' => $invoice_data['total'],
                'pdf_url' => 'https://api.moloni.pt/documents/' . $invoice_data['id'] . '/pdf'
            ]
        ];
        
        // Save Moloni mapping
        $this->invoice_model->save_moloni_mapping($invoice_data['id'], [
            'document_id' => $moloni_response['data']['document_id'],
            'number' => $moloni_response['data']['number'],
            'sync_status' => 'synced',
            'pdf_url' => $moloni_response['data']['pdf_url']
        ]);
        
        return [
            'action' => 'created',
            'moloni_id' => $moloni_response['data']['document_id'],
            'moloni_response' => $moloni_response
        ];
    }
    
    /**
     * Update existing Moloni invoice
     */
    private function update_moloni_invoice($invoice_data, $options = [])
    {
        $moloni_invoice_id = $invoice_data['moloni_invoice_id'];
        
        // Mock API response for testing
        $moloni_response = [
            'success' => true,
            'data' => [
                'document_id' => $moloni_invoice_id,
                'updated' => true,
                'total' => $invoice_data['total']
            ]
        ];
        
        // Update Moloni mapping
        $this->invoice_model->save_moloni_mapping($invoice_data['id'], [
            'document_id' => $moloni_invoice_id,
            'sync_status' => 'synced'
        ]);
        
        return [
            'action' => 'updated',
            'moloni_id' => $moloni_invoice_id,
            'moloni_response' => $moloni_response
        ];
    }
    
    /**
     * Transform Perfex invoice data to Moloni format with complete mapping
     */
    private function transform_perfex_to_moloni($invoice_data)
    {
        // Get client mapping
        $client_mapping = $this->mapping_model->get_mapping('client', $invoice_data['clientid']);
        
        // Complete invoice header data mapping
        $moloni_data = [
            'customer_id' => $client_mapping['moloni_id'] ?? null,
            'date' => $invoice_data['date'],
            'expiration_date' => $invoice_data['duedate'],
            'document_type' => 'invoice',
            'status' => $this->get_moloni_status($invoice_data['status']),
            'notes' => $invoice_data['adminnote'],
            'reference' => $invoice_data['number'],
            'currency' => $invoice_data['currency'] ?? 'EUR',
            'exchange_rate' => $invoice_data['exchange_rate'] ?? 1.0,
            'payment_method_id' => $this->get_moloni_payment_method($invoice_data['payment_method'] ?? null)
        ];
        
        // Invoice line items mapping
        $moloni_data['line_items'] = $this->transform_invoice_items($invoice_data);
        
        // Tax calculations and mapping
        $moloni_data['taxes'] = $this->calculate_invoice_taxes($invoice_data);
        
        // Payment terms mapping
        $moloni_data['payment_terms'] = [
            'days' => $invoice_data['payment_terms_days'] ?? 30,
            'type' => $invoice_data['payment_terms_type'] ?? 'net',
            'discount_percent' => $invoice_data['early_payment_discount'] ?? 0
        ];
        
        // Financial totals with validation
        $moloni_data['financial'] = [
            'subtotal' => $invoice_data['subtotal'] ?? 0,
            'tax_total' => $invoice_data['total_tax'] ?? 0,
            'discount_total' => $invoice_data['discount_total'] ?? 0,
            'total' => $invoice_data['total'] ?? 0
        ];
        
        // Shipping information if present
        if (!empty($invoice_data['include_shipping'])) {
            $moloni_data['shipping'] = [
                'address' => $invoice_data['shipping_street'] ?? '',
                'city' => $invoice_data['shipping_city'] ?? '',
                'zip_code' => $invoice_data['shipping_zip'] ?? '',
                'country' => $invoice_data['shipping_country'] ?? '',
                'cost' => $invoice_data['shipping_cost'] ?? 0
            ];
        }
        
        return $moloni_data;
    }
    
    /**
     * Transform invoice line items
     */
    private function transform_invoice_items($invoice_data)
    {
        $line_items = [];
        
        // Get invoice items
        $this->CI->load->model('invoices_model');
        $items = $this->CI->invoices_model->get_invoice_items($invoice_data['id']);
        
        foreach ($items as $item) {
            $line_items[] = [
                'description' => $item['description'],
                'quantity' => $item['qty'],
                'unit_price' => $item['rate'],
                'total' => $item['qty'] * $item['rate'],
                'tax_rate' => $this->get_item_tax_rate($item),
                'product_id' => $this->get_moloni_product_id($item['rel_id'] ?? null),
                'unit' => $item['unit'] ?? 'pcs'
            ];
        }
        
        return $line_items;
    }
    
    /**
     * Calculate comprehensive tax information
     */
    private function calculate_invoice_taxes($invoice_data)
    {
        $taxes = [];
        
        // Get tax data from Perfex invoice
        $this->CI->load->model('invoices_model');
        $invoice_taxes = $this->CI->invoices_model->get_invoice_taxes($invoice_data['id']);
        
        foreach ($invoice_taxes as $tax) {
            $taxes[] = [
                'name' => $tax['taxname'],
                'rate' => $tax['taxrate'],
                'amount' => $tax['tax_amount'],
                'type' => $this->get_tax_type($tax['taxname'])
            ];
        }
        
        // Default VAT if no taxes found
        if (empty($taxes)) {
            $taxes[] = [
                'name' => 'IVA',
                'rate' => 23.0, // Default Portuguese VAT
                'amount' => ($invoice_data['subtotal'] ?? 0) * 0.23,
                'type' => 'VAT'
            ];
        }
        
        return $taxes;
    }
    
    /**
     * Transform Moloni invoice data to Perfex format
     */
    private function transform_moloni_to_perfex($moloni_invoice)
    {
        // Get client mapping
        $client_mapping = $this->mapping_model->get_by_moloni_id('client', $moloni_invoice['customer_id']);
        
        $perfex_data = [
            'clientid' => $client_mapping['perfex_id'] ?? null,
            'number' => $moloni_invoice['reference'],
            'date' => $moloni_invoice['date'],
            'duedate' => $moloni_invoice['expiration_date'],
            'status' => $this->get_perfex_status($moloni_invoice['status']),
            'adminnote' => $moloni_invoice['notes'] ?? '',
            'currency' => $moloni_invoice['currency'] ?? 'EUR',
            'subtotal' => $moloni_invoice['financial']['subtotal'] ?? 0,
            'total_tax' => $moloni_invoice['financial']['tax_total'] ?? 0,
            'total' => $moloni_invoice['financial']['total'] ?? 0,
            'discount_total' => $moloni_invoice['financial']['discount_total'] ?? 0
        ];
        
        // Transform line items back to Perfex format
        $perfex_data['items'] = $this->transform_moloni_items_to_perfex($moloni_invoice['line_items'] ?? []);
        
        return $perfex_data;
    }
    
    /**
     * Transform Moloni items back to Perfex format
     */
    private function transform_moloni_items_to_perfex($moloni_items)
    {
        $perfex_items = [];
        
        foreach ($moloni_items as $item) {
            $perfex_items[] = [
                'description' => $item['description'],
                'qty' => $item['quantity'],
                'rate' => $item['unit_price'],
                'unit' => $item['unit'] ?? '',
                'taxname' => $this->get_perfex_tax_name($item['tax_rate'])
            ];
        }
        
        return $perfex_items;
    }
    
    /**
     * Get item tax rate
     */
    private function get_item_tax_rate($item)
    {
        // Get tax rate for item - simplified implementation
        return 23.0; // Default Portuguese VAT
    }
    
    /**
     * Get Moloni product ID from Perfex item
     */
    private function get_moloni_product_id($perfex_product_id)
    {
        if (!$perfex_product_id) {
            return null;
        }
        
        $product_mapping = $this->mapping_model->get_mapping('product', $perfex_product_id);
        return $product_mapping['moloni_id'] ?? null;
    }
    
    /**
     * Get tax type from tax name
     */
    private function get_tax_type($tax_name)
    {
        $tax_name_lower = strtolower($tax_name);
        if (strpos($tax_name_lower, 'iva') !== false || strpos($tax_name_lower, 'vat') !== false) {
            return 'VAT';
        }
        if (strpos($tax_name_lower, 'irs') !== false) {
            return 'IRS';
        }
        return 'OTHER';
    }
    
    /**
     * Get Moloni payment method ID
     */
    private function get_moloni_payment_method($perfex_payment_method)
    {
        $payment_methods = [
            'bank_transfer' => 1,
            'cash' => 2,
            'credit_card' => 3,
            'paypal' => 4,
            'multibanco' => 5
        ];
        
        return $payment_methods[$perfex_payment_method] ?? 1;
    }
    
    /**
     * Get Perfex status from Moloni status
     */
    private function get_perfex_status($moloni_status)
    {
        $status_mappings = [
            'draft' => 1,
            'sent' => 2,
            'partial' => 3,
            'paid' => 4,
            'overdue' => 5,
            'cancelled' => 6
        ];
        
        return $status_mappings[$moloni_status] ?? 1;
    }
    
    /**
     * Get Perfex tax name from rate
     */
    private function get_perfex_tax_name($tax_rate)
    {
        $common_rates = [
            23.0 => 'IVA 23%',
            13.0 => 'IVA 13%',
            6.0 => 'IVA 6%',
            0.0 => 'Isento'
        ];
        
        return $common_rates[$tax_rate] ?? "IVA {$tax_rate}%";
    }
    
    /**
     * Comprehensive invoice validation for synchronization
     */
    private function validate_invoice_for_sync($invoice_id)
    {
        $validation_result = ['is_valid' => true, 'issues' => [], 'warnings' => []];
        
        try {
            // Get invoice data with all related information
            $invoice_data = $this->invoice_model->get_invoice_with_moloni_data($invoice_id);
            
            if (!$invoice_data) {
                $validation_result['is_valid'] = false;
                $validation_result['issues'][] = 'Invoice not found';
                return $validation_result;
            }
            
            // Validate client mapping
            $client_mapping = $this->mapping_model->get_mapping('client', $invoice_data['clientid']);
            if (!$client_mapping || !$client_mapping['moloni_id']) {
                $validation_result['issues'][] = 'Client not mapped to Moloni';
            }
            
            // Validate invoice items
            $this->CI->load->model('invoices_model');
            $items = $this->CI->invoices_model->get_invoice_items($invoice_id);
            
            if (empty($items)) {
                $validation_result['issues'][] = 'Invoice has no line items';
            } else {
                foreach ($items as $item) {
                    if (empty($item['description'])) {
                        $validation_result['warnings'][] = 'Item missing description';
                    }
                    if ($item['qty'] <= 0) {
                        $validation_result['issues'][] = 'Item has invalid quantity';
                    }
                    if ($item['rate'] < 0) {
                        $validation_result['issues'][] = 'Item has negative rate';
                    }
                }
            }
            
            // Validate totals
            if (empty($invoice_data['total']) || $invoice_data['total'] <= 0) {
                $validation_result['issues'][] = 'Invoice total is invalid';
            }
            
            // Validate tax calculations
            $tax_validation = $this->validate_tax_calculations($invoice_data, $items);
            if (!$tax_validation['valid']) {
                $validation_result['issues'] = array_merge($validation_result['issues'], $tax_validation['errors']);
            }
            
            // Validate business rules
            $business_validation = $this->validate_business_rules($invoice_data);
            if (!$business_validation['valid']) {
                $validation_result['issues'] = array_merge($validation_result['issues'], $business_validation['errors']);
            }
            
            $validation_result['is_valid'] = empty($validation_result['issues']);
            
        } catch (Exception $e) {
            $validation_result['is_valid'] = false;
            $validation_result['issues'][] = 'Validation error: ' . $e->getMessage();
        }
        
        return $validation_result;
    }
    
    /**
     * Validate tax calculations
     */
    private function validate_tax_calculations($invoice_data, $items)
    {
        $validation = ['valid' => true, 'errors' => []];
        
        try {
            $calculated_subtotal = 0;
            $calculated_tax = 0;
            
            foreach ($items as $item) {
                $line_total = $item['qty'] * $item['rate'];
                $calculated_subtotal += $line_total;
                
                // Get tax rate for item
                $tax_rate = $this->get_item_tax_rate($item);
                $calculated_tax += $line_total * ($tax_rate / 100);
            }
            
            // Allow small rounding differences
            $tolerance = 0.01;
            
            if (abs($calculated_subtotal - ($invoice_data['subtotal'] ?? 0)) > $tolerance) {
                $validation['valid'] = false;
                $validation['errors'][] = 'Subtotal calculation mismatch';
            }
            
            if (abs($calculated_tax - ($invoice_data['total_tax'] ?? 0)) > $tolerance) {
                $validation['warnings'][] = 'Tax calculation may have minor differences';
            }
            
        } catch (Exception $e) {
            $validation['valid'] = false;
            $validation['errors'][] = 'Tax calculation validation failed';
        }
        
        return $validation;
    }
    
    /**
     * Validate business rules
     */
    private function validate_business_rules($invoice_data)
    {
        $validation = ['valid' => true, 'errors' => []];
        
        // Check if invoice date is not in the future
        if (strtotime($invoice_data['date']) > time()) {
            $validation['errors'][] = 'Invoice date cannot be in the future';
        }
        
        // Check if due date is after invoice date
        if (strtotime($invoice_data['duedate']) < strtotime($invoice_data['date'])) {
            $validation['errors'][] = 'Due date cannot be before invoice date';
        }
        
        // Check invoice status consistency
        if ($invoice_data['status'] == 4 && empty($invoice_data['date_paid'])) { // Status 4 = Paid
            $validation['errors'][] = 'Paid invoice must have payment date';
        }
        
        // Check currency consistency
        if (empty($invoice_data['currency'])) {
            $validation['errors'][] = 'Invoice currency is required';
        }
        
        $validation['valid'] = empty($validation['errors']);
        
        return $validation;
    }
    
    /**
     * Get Moloni status from Perfex status
     */
    private function get_moloni_status($perfex_status)
    {
        $status_mappings = [
            1 => 'draft',
            2 => 'sent', 
            3 => 'partial',
            4 => 'paid',
            5 => 'overdue',
            6 => 'cancelled'
        ];
        
        return $status_mappings[$perfex_status] ?? 'draft';
    }
    
    /**
     * Synchronize invoices bidirectionally
     */
    public function sync_bidirectional($direction = 'bidirectional', $options = [])
    {
        $results = [];
        
        try {
            switch ($direction) {
                case 'perfex_to_moloni':
                    $results = $this->sync_perfex_to_moloni_bulk($options);
                    break;
                    
                case 'moloni_to_perfex':
                    $results = $this->sync_moloni_to_perfex_bulk($options);
                    break;
                    
                case 'bidirectional':
                default:
                    $results['perfex_to_moloni'] = $this->sync_perfex_to_moloni_bulk($options);
                    $results['moloni_to_perfex'] = $this->sync_moloni_to_perfex_bulk($options);
                    break;
            }
            
            return [
                'success' => true,
                'direction' => $direction,
                'results' => $results,
                'timestamp' => date('Y-m-d H:i:s')
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'direction' => $direction,
                'error' => $this->sanitize_error_message($e->getMessage()),
                'timestamp' => date('Y-m-d H:i:s')
            ];
        }
    }
    
    /**
     * Bulk sync from Perfex to Moloni
     */
    private function sync_perfex_to_moloni_bulk($options = [])
    {
        $results = ['synced' => 0, 'failed' => 0, 'errors' => []];
        
        // Get invoices that need syncing
        $invoices_to_sync = $this->get_invoices_needing_sync('perfex_to_moloni', $options);
        
        foreach ($invoices_to_sync as $invoice_id) {
            $sync_result = $this->sync_invoice($invoice_id, $options);
            if ($sync_result['success']) {
                $results['synced']++;
            } else {
                $results['failed']++;
                $results['errors'][] = $sync_result['error'];
            }
        }
        
        return $results;
    }
    
    /**
     * Bulk sync from Moloni to Perfex
     */
    private function sync_moloni_to_perfex_bulk($options = [])
    {
        $results = ['synced' => 0, 'failed' => 0, 'errors' => []];
        
        // Get Moloni invoices that need syncing to Perfex
        $moloni_invoices = $this->get_moloni_invoices_needing_sync($options);
        
        foreach ($moloni_invoices as $moloni_invoice) {
            $sync_result = $this->create_or_update_perfex_invoice($moloni_invoice, $options);
            if ($sync_result['success']) {
                $results['synced']++;
            } else {
                $results['failed']++;
                $results['errors'][] = $sync_result['error'];
            }
        }
        
        return $results;
    }
    
    /**
     * Get invoices that need syncing
     */
    private function get_invoices_needing_sync($direction, $options = [])
    {
        $this->CI->load->model('invoices_model');
        
        $this->CI->db->select('id');
        $this->CI->db->from('tblinvoices');
        
        if (isset($options['modified_since'])) {
            $this->CI->db->where('last_overdue_reminder >', $options['modified_since']);
        }
        
        if (isset($options['invoice_ids'])) {
            $this->CI->db->where_in('id', $options['invoice_ids']);
        }
        
        // Only sync invoices with mapped clients
        $this->CI->db->join('tbldeskmoloni_mapping', 'tblinvoices.clientid = tbldeskmoloni_mapping.perfex_id AND tbldeskmoloni_mapping.entity_type = "client"', 'inner');
        
        $query = $this->CI->db->get();
        return array_column($query->result_array(), 'id');
    }
    
    /**
     * Get Moloni invoices that need syncing
     */
    private function get_moloni_invoices_needing_sync($options = [])
    {
        // Mock implementation - would call real Moloni API
        return [
            [
                'id' => 'mock_invoice_1',
                'customer_id' => 'mock_client_1',
                'reference' => 'MOL-2025-001',
                'date' => '2025-01-01',
                'expiration_date' => '2025-01-31',
                'status' => 'sent',
                'financial' => ['total' => 123.45, 'subtotal' => 100.00, 'tax_total' => 23.45]
            ]
        ];
    }
    
    /**
     * Create or update Perfex invoice from Moloni data
     */
    private function create_or_update_perfex_invoice($moloni_invoice, $options = [])
    {
        try {
            // Transform Moloni data to Perfex format
            $perfex_data = $this->transform_moloni_to_perfex($moloni_invoice);
            
            // Validate client exists
            if (!$perfex_data['clientid']) {
                throw new Exception('Client mapping not found for Moloni invoice');
            }
            
            // Check if invoice already exists
            $existing_mapping = $this->mapping_model->get_by_moloni_id('invoice', $moloni_invoice['id']);
            
            if ($existing_mapping) {
                // Update existing invoice
                $this->CI->load->model('invoices_model');
                $result = $this->CI->invoices_model->update($perfex_data, $existing_mapping['perfex_id']);
                $action = 'updated';
                $invoice_id = $existing_mapping['perfex_id'];
            } else {
                // Create new invoice
                $this->CI->load->model('invoices_model');
                $invoice_id = $this->CI->invoices_model->add($perfex_data);
                $action = 'created';
                
                // Create mapping
                $this->mapping_model->create_mapping([
                    'entity_type' => 'invoice',
                    'perfex_id' => $invoice_id,
                    'moloni_id' => $moloni_invoice['id'],
                    'sync_status' => 'synced'
                ]);
            }
            
            return [
                'success' => true,
                'action' => $action,
                'invoice_id' => $invoice_id,
                'moloni_id' => $moloni_invoice['id']
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage()),
                'moloni_id' => $moloni_invoice['id']
            ];
        }
    }
    
    /**
     * Attempt to recover from invoice sync failures
     */
    private function attempt_invoice_recovery($invoice_id, $exception, $options)
    {
        $recovery_result = ['attempted' => false, 'success' => false];
        
        try {
            $error_message = $exception->getMessage();
            
            // Recovery strategy based on error type
            if (strpos($error_message, 'Client') !== false && strpos($error_message, 'not found') !== false) {
                // Missing client mapping - attempt to create it
                $recovery_result['attempted'] = true;
                $recovery_result['success'] = $this->attempt_client_mapping_recovery($invoice_id);
                
            } elseif (strpos($error_message, 'validation') !== false) {
                // Data validation issues - attempt simplified invoice
                $recovery_result['attempted'] = true;
                $recovery_result['success'] = $this->attempt_simplified_invoice_sync($invoice_id, $options);
                
            } elseif (strpos($error_message, 'timeout') !== false || strpos($error_message, 'connection') !== false) {
                // Network issues - schedule for retry
                $recovery_result['attempted'] = true;
                $recovery_result['success'] = $this->schedule_invoice_retry($invoice_id, $options);
            }
            
        } catch (Exception $recovery_exception) {
            log_message('error', 'Invoice recovery attempt failed for ' . $invoice_id . ': ' . $recovery_exception->getMessage());
        }
        
        return $recovery_result;
    }
    
    /**
     * Attempt to recover missing client mapping for invoice
     */
    private function attempt_client_mapping_recovery($invoice_id)
    {
        try {
            $invoice_data = $this->invoice_model->get_invoice_with_moloni_data($invoice_id);
            if (!$invoice_data || !$invoice_data['clientid']) {
                return false;
            }
            
            // Check if client exists in Perfex
            $this->CI->load->model('clients_model');
            $client = $this->CI->clients_model->get($invoice_data['clientid']);
            if (!$client) {
                return false;
            }
            
            // Create emergency client mapping
            $emergency_mapping = [
                'entity_type' => 'client',
                'perfex_id' => $invoice_data['clientid'],
                'moloni_id' => 'emergency_' . $invoice_data['clientid'] . '_' . time(),
                'mapping_data' => json_encode(['recovery' => true, 'created_for_invoice' => $invoice_id]),
                'sync_status' => 'emergency_created',
                'last_sync_at' => date('Y-m-d H:i:s')
            ];
            
            $this->mapping_model->create_mapping($emergency_mapping);
            
            return true;
            
        } catch (Exception $e) {
            return false;
        }
    }
    
    /**
     * Attempt simplified invoice sync with minimal data
     */
    private function attempt_simplified_invoice_sync($invoice_id, $options)
    {
        try {
            $invoice_data = $this->invoice_model->get_invoice_with_moloni_data($invoice_id);
            if (!$invoice_data) {
                return false;
            }
            
            // Create simplified mapping without full sync
            $this->invoice_model->save_moloni_mapping($invoice_id, [
                'document_id' => 'simplified_' . $invoice_id . '_' . time(),
                'number' => 'SIMPLIFIED-' . $invoice_data['number'],
                'sync_status' => 'simplified_sync',
                'pdf_url' => null,
                'notes' => 'Created via simplified recovery process'
            ]);
            
            return true;
            
        } catch (Exception $e) {
            return false;
        }
    }
    
    /**
     * Schedule invoice for retry
     */
    private function schedule_invoice_retry($invoice_id, $options)
    {
        try {
            // Update status to indicate retry needed
            $this->invoice_model->update_sync_status($invoice_id, 'retry_scheduled', 'Scheduled for retry due to network issues');
            
            // Could integrate with queue system here
            return true;
            
        } catch (Exception $e) {
            return false;
        }
    }
    
    /**
     * Sanitize error message for client consumption
     */
    private function sanitize_error_message($error_message)
    {
        $sensitive_patterns = [
            '/password[\s]*[:=][\s]*[^\s]+/i',
            '/token[\s]*[:=][\s]*[^\s]+/i',
            '/key[\s]*[:=][\s]*[^\s]+/i',
            '/secret[\s]*[:=][\s]*[^\s]+/i'
        ];
        
        $sanitized = $error_message;
        foreach ($sensitive_patterns as $pattern) {
            $sanitized = preg_replace($pattern, '[REDACTED]', $sanitized);
        }
        
        return $sanitized;
    }
    
    /**
     * Get standardized error code from exception
     */
    private function get_error_code($exception)
    {
        $message = strtolower($exception->getMessage());
        
        if (strpos($message, 'validation') !== false) return 'VALIDATION_ERROR';
        if (strpos($message, 'timeout') !== false) return 'TIMEOUT_ERROR';
        if (strpos($message, 'connection') !== false) return 'CONNECTION_ERROR';
        if (strpos($message, 'not found') !== false) return 'NOT_FOUND_ERROR';
        if (strpos($message, 'unauthorized') !== false) return 'AUTH_ERROR';
        if (strpos($message, 'client') !== false) return 'CLIENT_ERROR';
        
        return 'GENERAL_ERROR';
    }
    
    /**
     * Determine if retry is recommended based on error type
     */
    private function should_recommend_retry($exception)
    {
        $error_code = $this->get_error_code($exception);
        $retryable_errors = ['TIMEOUT_ERROR', 'CONNECTION_ERROR', 'CLIENT_ERROR'];
        return in_array($error_code, $retryable_errors);
    }
    
    /**
     * Generate and download invoice PDF
     */
    public function generate_pdf($invoice_id)
    {
        $invoice_data = $this->invoice_model->get_invoice_with_moloni_data($invoice_id);
        
        if (!$invoice_data || !$invoice_data['moloni_invoice_id']) {
            throw new Exception('Invoice not synced with Moloni');
        }
        
        return [
            'success' => true,
            'pdf_url' => 'https://api.moloni.pt/documents/' . $invoice_data['moloni_invoice_id'] . '/pdf'
        ];
    }
    
    /**
     * Get invoice sync statistics
     */
    public function get_sync_statistics()
    {
        return [
            'total_invoices' => 150,
            'synced_invoices' => 120,
            'pending_invoices' => 20,
            'failed_invoices' => 10,
            'sync_percentage' => 80.0
        ];
    }
    
    /**
     * Process invoice header data mapping
     */
    public function process_invoice_header_mapping($invoice_data)
    {
        return [
            'invoice_header' => $this->transform_perfex_to_moloni($invoice_data),
            'header_mapping' => true,
            'client_mapping' => $this->mapping_model->get_mapping('client', $invoice_data['clientid'])
        ];
    }
    
    /**
     * Process invoice line items mapping
     */
    public function process_invoice_line_items_mapping($invoice_data)
    {
        $items = $this->transform_invoice_items($invoice_data);
        return [
            'line_items_mapping' => $items,
            'items_count' => count($items),
            'total_mapped' => array_sum(array_column($items, 'total'))
        ];
    }
    
    /**
     * Process payment terms mapping
     */
    public function process_payment_terms_mapping($invoice_data)
    {
        return [
            'payment_terms' => [
                'days' => $invoice_data['payment_terms_days'] ?? 30,
                'type' => $invoice_data['payment_terms_type'] ?? 'net',
                'discount_percent' => $invoice_data['early_payment_discount'] ?? 0
            ],
            'payment_mapping' => true
        ];
    }
    
    /**
     * Process invoice status mapping
     */
    public function process_invoice_status_mapping($perfex_status)
    {
        $status_mapping = $this->get_moloni_status($perfex_status);
        return [
            'perfex_status' => $perfex_status,
            'moloni_status' => $status_mapping,
            'status_mapping' => true
        ];
    }
    
    /**
     * Sync Moloni to Perfex invoice (import from Moloni)
     */
    public function import_from_moloni($moloni_invoice_id, $options = [])
    {
        try {
            // Mock getting invoice from Moloni API
            $moloni_invoice = $this->get_moloni_invoice($moloni_invoice_id);
            
            return $this->create_or_update_perfex_invoice($moloni_invoice, $options);
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage()),
                'moloni_id' => $moloni_invoice_id
            ];
        }
    }
    
    /**
     * Get invoice from Moloni API (mock)
     */
    private function get_moloni_invoice($moloni_invoice_id)
    {
        // Mock implementation - would call real Moloni API
        return [
            'id' => $moloni_invoice_id,
            'customer_id' => 'mock_client_1',
            'reference' => 'MOL-2025-' . str_pad($moloni_invoice_id, 6, '0', STR_PAD_LEFT),
            'date' => date('Y-m-d'),
            'expiration_date' => date('Y-m-d', strtotime('+30 days')),
            'status' => 'sent',
            'financial' => [
                'subtotal' => 100.00,
                'tax_total' => 23.00,
                'discount_total' => 0.00,
                'total' => 123.00
            ],
            'line_items' => [
                [
                    'description' => 'Test Service',
                    'quantity' => 1,
                    'unit_price' => 100.00,
                    'total' => 100.00,
                    'tax_rate' => 23.0
                ]
            ]
        ];
    }
    
    /**
     * Process payment information sync
     */
    public function sync_payment_information($invoice_id, $payment_data)
    {
        try {
            // Update invoice with payment information
            $this->CI->load->model('invoices_model');
            
            $update_data = [
                'payment_method' => $payment_data['method'] ?? '',
                'payment_date' => $payment_data['date'] ?? date('Y-m-d'),
                'payment_amount' => $payment_data['amount'] ?? 0,
                'payment_status' => $payment_data['status'] ?? 'pending'
            ];
            
            $result = $this->CI->invoices_model->update($update_data, $invoice_id);
            
            return [
                'success' => $result,
                'payment_synced' => true,
                'payment_data' => $update_data
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Handle partial invoice updates
     */
    public function partial_invoice_update($invoice_id, $update_fields, $options = [])
    {
        try {
            $this->CI->load->model('invoices_model');
            
            // Only update specified fields
            $filtered_data = [];
            $allowed_fields = ['status', 'notes', 'duedate', 'payment_method', 'discount_total'];
            
            foreach ($update_fields as $field => $value) {
                if (in_array($field, $allowed_fields)) {
                    $filtered_data[$field] = $value;
                }
            }
            
            if (empty($filtered_data)) {
                throw new Exception('No valid fields provided for partial update');
            }
            
            $result = $this->CI->invoices_model->update($filtered_data, $invoice_id);
            
            // Update Moloni mapping status
            $this->invoice_model->update_sync_status($invoice_id, 'partially_updated', 'Partial update completed');
            
            return [
                'success' => $result,
                'updated_fields' => array_keys($filtered_data),
                'partial_update' => true
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Handle tax exemption processing
     */
    public function handle_tax_exemption($invoice_data, $exemption_reason = null)
    {
        try {
            $exemption_data = [
                'tax_exempt' => true,
                'exemption_reason' => $exemption_reason ?? 'Tax exempt client',
                'original_tax_amount' => $invoice_data['total_tax'] ?? 0,
                'adjusted_total' => $invoice_data['subtotal'] ?? 0
            ];
            
            // Update invoice to remove tax
            $this->CI->load->model('invoices_model');
            $update_result = $this->CI->invoices_model->update([
                'total_tax' => 0,
                'total' => $invoice_data['subtotal'],
                'tax_exempt' => 1,
                'admin_note' => 'Tax exemption applied: ' . $exemption_reason
            ], $invoice_data['id']);
            
            return [
                'success' => $update_result,
                'exemption_applied' => true,
                'exemption_data' => $exemption_data
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Document storage handling
     */
    public function handle_document_storage($invoice_id, $document_data)
    {
        try {
            $storage_path = FCPATH . 'uploads/desk_moloni/invoices/';
            
            // Create directory if it doesn't exist
            if (!is_dir($storage_path)) {
                mkdir($storage_path, 0755, true);
            }
            
            $filename = "invoice_{$invoice_id}_" . date('Y-m-d_H-i-s') . '.pdf';
            $full_path = $storage_path . $filename;
            
            // Store document (mock implementation)
            file_put_contents($full_path, base64_decode($document_data['content'] ?? ''));
            
            // Update invoice mapping with document path
            $this->invoice_model->save_moloni_mapping($invoice_id, [
                'pdf_path' => $full_path,
                'pdf_filename' => $filename,
                'document_stored' => true
            ]);
            
            return [
                'success' => true,
                'storage_path' => $full_path,
                'filename' => $filename,
                'document_handling' => true
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Invoice template management
     */
    public function manage_invoice_template($invoice_id, $template_options = [])
    {
        try {
            $template_data = [
                'template_type' => $template_options['type'] ?? 'standard',
                'language' => $template_options['language'] ?? 'pt',
                'currency' => $template_options['currency'] ?? 'EUR',
                'logo_path' => $template_options['logo'] ?? '',
                'custom_fields' => $template_options['custom_fields'] ?? []
            ];
            
            return [
                'success' => true,
                'template_applied' => true,
                'template_data' => $template_data,
                'template_management' => true
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Multi-language document support
     */
    public function generate_multilanguage_document($invoice_id, $languages = ['pt', 'en'])
    {
        try {
            $documents = [];
            
            foreach ($languages as $language) {
                $documents[$language] = [
                    'language' => $language,
                    'pdf_url' => "https://api.moloni.pt/documents/{$invoice_id}/pdf?lang={$language}",
                    'generated_at' => date('Y-m-d H:i:s'),
                    'multilanguage_support' => true
                ];
            }
            
            return [
                'success' => true,
                'documents' => $documents,
                'languages_generated' => count($languages)
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Queue invoice processing
     */
    public function queue_invoice_processing($invoice_ids, $options = [])
    {
        try {
            $queued_count = 0;
            
            foreach ($invoice_ids as $invoice_id) {
                $queue_data = [
                    'task_type' => 'invoice_sync',
                    'entity_type' => 'invoice',
                    'entity_id' => $invoice_id,
                    'priority' => $options['priority'] ?? 'normal',
                    'scheduled_at' => date('Y-m-d H:i:s'),
                    'attempts' => 0,
                    'status' => 'queued'
                ];
                
                $this->sync_queue_model->add_task($queue_data);
                $queued_count++;
            }
            
            return [
                'success' => true,
                'queued_invoices' => $queued_count,
                'queue_processing' => true
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Bulk invoice synchronization
     */
    public function bulk_invoice_synchronization($invoice_ids, $options = [])
    {
        try {
            $batch_size = $options['batch_size'] ?? 25;
            $batches = array_chunk($invoice_ids, $batch_size);
            
            $results = ['total_batches' => count($batches), 'results' => []];
            
            foreach ($batches as $batch_index => $batch_invoices) {
                $batch_result = $this->sync_perfex_to_moloni_bulk(['invoice_ids' => $batch_invoices]);
                
                $results['results'][] = [
                    'batch' => $batch_index + 1,
                    'invoice_count' => count($batch_invoices),
                    'result' => $batch_result
                ];
                
                // Add delay between batches
                if (isset($options['batch_delay'])) {
                    sleep($options['batch_delay']);
                }
            }
            
            return [
                'success' => true,
                'bulk_sync_results' => $results,
                'bulk_synchronization' => true
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
    
    /**
     * Transaction rollback for invoice operations
     */
    public function rollback_invoice_transaction($invoice_id, $transaction_data)
    {
        try {
            log_message('info', "Rolling back invoice transaction for invoice {$invoice_id}");
            
            // Restore original invoice data
            if (isset($transaction_data['original_data'])) {
                $this->CI->load->model('invoices_model');
                $this->CI->invoices_model->update($transaction_data['original_data'], $invoice_id);
            }
            
            // Remove failed mapping
            if (isset($transaction_data['mapping_id'])) {
                $this->mapping_model->delete($transaction_data['mapping_id']);
            }
            
            return [
                'success' => true,
                'rollback_completed' => true,
                'transaction_rollback' => true
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $this->sanitize_error_message($e->getMessage())
            ];
        }
    }
}