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

<?php

/**
 * Perfex Hooks Integration
 * Handles Perfex CRM hooks for automatic synchronization triggers
 * 
 * @package DeskMoloni
 * @subpackage Libraries
 * @category HooksIntegration
 * @author Descomplicar® - PHP Fullstack Engineer
 * @version 1.0.0
 */

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

class PerfexHooks
{
    protected $CI;
    protected $queue_processor;
    protected $entity_mapping;
    protected $error_handler;
    protected $model;
    
    // Hook priority levels
    const PRIORITY_LOW = 1;
    const PRIORITY_NORMAL = 2;
    const PRIORITY_HIGH = 3;
    const PRIORITY_CRITICAL = 4;
    
    // Sync delay settings (in seconds)
    const DEFAULT_SYNC_DELAY = 300; // 5 minutes
    const CRITICAL_SYNC_DELAY = 60; // 1 minute
    const BULK_SYNC_DELAY = 600; // 10 minutes
    
    public function __construct()
    {
        $this->CI = &get_instance();

        // Load base model for local use
        if (method_exists($this->CI, 'load')) {
            $this->CI->load->model('desk_moloni/desk_moloni_sync_log_model', 'desk_moloni_sync_log_model');
            $this->model = $this->CI->desk_moloni_sync_log_model;
        }

        // Initialize dependencies for QueueProcessor
        $this->CI->load->model('desk_moloni/desk_moloni_model');
        $model = $this->CI->desk_moloni_model;

        // Redis initialization
        if (!extension_loaded('redis')) {
            throw new \Exception('Redis extension not loaded');
        }
        $redis = new \Redis();
        $redis_host = get_option('desk_moloni_redis_host', '127.0.0.1');
        $redis_port = (int)get_option('desk_moloni_redis_port', 6379);
        $redis_password = get_option('desk_moloni_redis_password', '');
        $redis_db = (int)get_option('desk_moloni_redis_db', 0);
        if (!$redis->connect($redis_host, $redis_port, 2.5)) {
            throw new \Exception('Failed to connect to Redis server');
        }
        if (!empty($redis_password)) {
            $redis->auth($redis_password);
        }
        $redis->select($redis_db);

        // Instantiate services
        $this->entity_mapping = new EntityMappingService();
        $this->error_handler = new ErrorHandler();
        $retry_handler = new RetryHandler();

        // Instantiate QueueProcessor with dependencies
        $this->queue_processor = new QueueProcessor(
            $redis,
            $model,
            $this->entity_mapping,
            $this->error_handler,
            $retry_handler
        );

        $this->register_hooks();

        log_activity('PerfexHooks initialized and registered with DI');
    }
    
    /**
     * Register all Perfex CRM hooks
     */
    protected function register_hooks()
    {
        // Client/Customer hooks
        hooks()->add_action('after_client_added', [$this, 'handle_client_added']);
        hooks()->add_action('after_client_updated', [$this, 'handle_client_updated']);
        hooks()->add_action('before_client_deleted', [$this, 'handle_client_before_delete']);
        
        // Invoice hooks
        hooks()->add_action('after_invoice_added', [$this, 'handle_invoice_added']);
        hooks()->add_action('after_invoice_updated', [$this, 'handle_invoice_updated']);
        hooks()->add_action('invoice_status_changed', [$this, 'handle_invoice_status_changed']);
        hooks()->add_action('invoice_payment_recorded', [$this, 'handle_invoice_payment_recorded']);
        
        // Estimate hooks
        hooks()->add_action('after_estimate_added', [$this, 'handle_estimate_added']);
        hooks()->add_action('after_estimate_updated', [$this, 'handle_estimate_updated']);
        hooks()->add_action('estimate_status_changed', [$this, 'handle_estimate_status_changed']);
        
        // Credit Note hooks
        hooks()->add_action('after_credit_note_added', [$this, 'handle_credit_note_added']);
        hooks()->add_action('after_credit_note_updated', [$this, 'handle_credit_note_updated']);
        
        // Item/Product hooks
        hooks()->add_action('after_item_added', [$this, 'handle_item_added']);
        hooks()->add_action('after_item_updated', [$this, 'handle_item_updated']);
        hooks()->add_action('before_item_deleted', [$this, 'handle_item_before_delete']);
        
        // Contact hooks
        hooks()->add_action('after_contact_added', [$this, 'handle_contact_added']);
        hooks()->add_action('after_contact_updated', [$this, 'handle_contact_updated']);
        
        // Payment hooks
        hooks()->add_action('after_payment_added', [$this, 'handle_payment_added']);
        hooks()->add_action('after_payment_updated', [$this, 'handle_payment_updated']);
        
        // Custom hooks for Moloni integration
        hooks()->add_action('desk_moloni_webhook_received', [$this, 'handle_moloni_webhook']);
        hooks()->add_action('desk_moloni_manual_sync_requested', [$this, 'handle_manual_sync']);
        
        log_activity('Perfex CRM hooks registered successfully');
    }
    
    /**
     * Handle client added event
     * 
     * @param int $client_id
     */
    public function handle_client_added($client_id)
    {
        if (!$this->should_sync_entity('customers')) {
            return;
        }
        
        try {
            $priority = $this->get_sync_priority('customer', 'create');
            $delay = $this->get_sync_delay('customer', 'create');
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_CUSTOMER,
                $client_id,
                'create',
                'perfex_to_moloni',
                $priority,
                ['trigger' => 'client_added'],
                $delay
            );
            
            if ($job_id) {
                log_activity("Client #{$client_id} queued for sync to Moloni (Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'CLIENT_ADDED_HOOK_FAILED',
                $e->getMessage(),
                ['client_id' => $client_id]
            );
        }
    }
    
    /**
     * Handle client updated event
     * 
     * @param int $client_id
     * @param array $data
     */
    public function handle_client_updated($client_id, $data = [])
    {
        if (!$this->should_sync_entity('customers')) {
            return;
        }
        
        try {
            // Check if significant fields were changed
            if (!$this->has_significant_changes('customer', $data)) {
                log_activity("Client #{$client_id} updated but no significant changes detected");
                return;
            }
            
            $priority = $this->get_sync_priority('customer', 'update');
            $delay = $this->get_sync_delay('customer', 'update');
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_CUSTOMER,
                $client_id,
                'update',
                'perfex_to_moloni',
                $priority,
                [
                    'trigger' => 'client_updated',
                    'changed_fields' => array_keys($data)
                ],
                $delay
            );
            
            if ($job_id) {
                log_activity("Client #{$client_id} queued for update sync to Moloni (Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'CLIENT_UPDATED_HOOK_FAILED',
                $e->getMessage(),
                ['client_id' => $client_id, 'data' => $data]
            );
        }
    }
    
    /**
     * Handle client before delete event
     * 
     * @param int $client_id
     */
    public function handle_client_before_delete($client_id)
    {
        if (!$this->should_sync_entity('customers')) {
            return;
        }
        
        try {
            // Check if client is mapped to Moloni
            $mapping = $this->entity_mapping->get_mapping_by_perfex_id(
                EntityMappingService::ENTITY_CUSTOMER,
                $client_id
            );
            
            if (!$mapping) {
                return; // No mapping, nothing to sync
            }
            
            $priority = QueueProcessor::PRIORITY_HIGH; // High priority for deletions
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_CUSTOMER,
                $client_id,
                'delete',
                'perfex_to_moloni',
                $priority,
                [
                    'trigger' => 'client_before_delete',
                    'moloni_id' => $mapping->moloni_id
                ],
                0 // No delay for deletions
            );
            
            if ($job_id) {
                log_activity("Client #{$client_id} queued for deletion sync to Moloni (Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'CLIENT_DELETE_HOOK_FAILED',
                $e->getMessage(),
                ['client_id' => $client_id]
            );
        }
    }
    
    /**
     * Handle invoice added event
     * 
     * @param int $invoice_id
     */
    public function handle_invoice_added($invoice_id)
    {
        if (!$this->should_sync_entity('invoices')) {
            return;
        }
        
        try {
            $priority = QueueProcessor::PRIORITY_HIGH; // Invoices are high priority
            $delay = $this->get_sync_delay('invoice', 'create');
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_INVOICE,
                $invoice_id,
                'create',
                'perfex_to_moloni',
                $priority,
                ['trigger' => 'invoice_added'],
                $delay
            );
            
            if ($job_id) {
                log_activity("Invoice #{$invoice_id} queued for sync to Moloni (Job: {$job_id})");
                
                // Also sync client if not already synced
                $this->ensure_client_synced_for_invoice($invoice_id);
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'INVOICE_ADDED_HOOK_FAILED',
                $e->getMessage(),
                ['invoice_id' => $invoice_id]
            );
        }
    }
    
    /**
     * Handle invoice updated event
     * 
     * @param int $invoice_id
     * @param array $data
     */
    public function handle_invoice_updated($invoice_id, $data = [])
    {
        if (!$this->should_sync_entity('invoices')) {
            return;
        }
        
        try {
            // Get invoice status to determine sync behavior
            $this->CI->load->model('invoices_model');
            $invoice = $this->CI->invoices_model->get($invoice_id);
            
            if (!$invoice) {
                return;
            }
            
            $priority = $this->get_invoice_update_priority($invoice, $data);
            $delay = $this->get_sync_delay('invoice', 'update');
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_INVOICE,
                $invoice_id,
                'update',
                'perfex_to_moloni',
                $priority,
                [
                    'trigger' => 'invoice_updated',
                    'invoice_status' => $invoice->status,
                    'changed_fields' => array_keys($data)
                ],
                $delay
            );
            
            if ($job_id) {
                log_activity("Invoice #{$invoice_id} queued for update sync to Moloni (Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'INVOICE_UPDATED_HOOK_FAILED',
                $e->getMessage(),
                ['invoice_id' => $invoice_id, 'data' => $data]
            );
        }
    }
    
    /**
     * Handle invoice status changed event
     * 
     * @param int $invoice_id
     * @param int $old_status
     * @param int $new_status
     */
    public function handle_invoice_status_changed($invoice_id, $old_status, $new_status)
    {
        if (!$this->should_sync_entity('invoices')) {
            return;
        }
        
        try {
            // Critical status changes should sync immediately
            $critical_statuses = [2, 3, 4, 5]; // Sent, Paid, Overdue, Cancelled
            $priority = in_array($new_status, $critical_statuses) ? 
                       QueueProcessor::PRIORITY_CRITICAL : 
                       QueueProcessor::PRIORITY_HIGH;
            
            $delay = $priority === QueueProcessor::PRIORITY_CRITICAL ? 0 : self::CRITICAL_SYNC_DELAY;
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_INVOICE,
                $invoice_id,
                'update',
                'perfex_to_moloni',
                $priority,
                [
                    'trigger' => 'invoice_status_changed',
                    'old_status' => $old_status,
                    'new_status' => $new_status
                ],
                $delay
            );
            
            if ($job_id) {
                log_activity("Invoice #{$invoice_id} status change queued for sync (Status: {$old_status} -> {$new_status}, Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'INVOICE_STATUS_HOOK_FAILED',
                $e->getMessage(),
                ['invoice_id' => $invoice_id, 'old_status' => $old_status, 'new_status' => $new_status]
            );
        }
    }
    
    /**
     * Handle invoice payment recorded event
     * 
     * @param int $payment_id
     * @param int $invoice_id
     */
    public function handle_invoice_payment_recorded($payment_id, $invoice_id)
    {
        if (!$this->should_sync_entity('payments')) {
            return;
        }
        
        try {
            // Payment recording is critical for financial accuracy
            $priority = QueueProcessor::PRIORITY_CRITICAL;
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_INVOICE,
                $invoice_id,
                'update',
                'perfex_to_moloni',
                $priority,
                [
                    'trigger' => 'payment_recorded',
                    'payment_id' => $payment_id
                ],
                0 // No delay for payments
            );
            
            if ($job_id) {
                log_activity("Invoice #{$invoice_id} payment recorded, queued for sync (Payment: #{$payment_id}, Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'PAYMENT_RECORDED_HOOK_FAILED',
                $e->getMessage(),
                ['payment_id' => $payment_id, 'invoice_id' => $invoice_id]
            );
        }
    }
    
    /**
     * Handle estimate added event
     * 
     * @param int $estimate_id
     */
    public function handle_estimate_added($estimate_id)
    {
        if (!$this->should_sync_entity('estimates')) {
            return;
        }
        
        try {
            $priority = QueueProcessor::PRIORITY_NORMAL;
            $delay = $this->get_sync_delay('estimate', 'create');
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_ESTIMATE,
                $estimate_id,
                'create',
                'perfex_to_moloni',
                $priority,
                ['trigger' => 'estimate_added'],
                $delay
            );
            
            if ($job_id) {
                log_activity("Estimate #{$estimate_id} queued for sync to Moloni (Job: {$job_id})");
                
                // Ensure client is synced
                $this->ensure_client_synced_for_estimate($estimate_id);
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'ESTIMATE_ADDED_HOOK_FAILED',
                $e->getMessage(),
                ['estimate_id' => $estimate_id]
            );
        }
    }
    
    /**
     * Handle item/product added event
     * 
     * @param int $item_id
     */
    public function handle_item_added($item_id)
    {
        if (!$this->should_sync_entity('products')) {
            return;
        }
        
        try {
            $priority = QueueProcessor::PRIORITY_NORMAL;
            $delay = $this->get_sync_delay('product', 'create');
            
            $job_id = $this->queue_processor->add_to_queue(
                EntityMappingService::ENTITY_PRODUCT,
                $item_id,
                'create',
                'perfex_to_moloni',
                $priority,
                ['trigger' => 'item_added'],
                $delay
            );
            
            if ($job_id) {
                log_activity("Item #{$item_id} queued for sync to Moloni (Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'ITEM_ADDED_HOOK_FAILED',
                $e->getMessage(),
                ['item_id' => $item_id]
            );
        }
    }
    
    /**
     * Handle Moloni webhook events
     * 
     * @param array $webhook_data
     */
    public function handle_moloni_webhook($webhook_data)
    {
        try {
            $entity_type = $webhook_data['entity_type'] ?? null;
            $entity_id = $webhook_data['entity_id'] ?? null;
            $action = $webhook_data['action'] ?? null;
            
            if (!$entity_type || !$entity_id || !$action) {
                throw new \Exception('Invalid webhook data structure');
            }
            
            // Determine priority based on entity type and action
            $priority = $this->get_webhook_priority($entity_type, $action);
            
            $job_id = $this->queue_processor->add_to_queue(
                $entity_type,
                $entity_id,
                $action,
                'moloni_to_perfex',
                $priority,
                [
                    'trigger' => 'moloni_webhook',
                    'webhook_data' => $webhook_data
                ],
                0 // No delay for webhooks
            );
            
            if ($job_id) {
                log_activity("Moloni webhook processed: {$entity_type} #{$entity_id} {$action} (Job: {$job_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'MOLONI_WEBHOOK_HOOK_FAILED',
                $e->getMessage(),
                ['webhook_data' => $webhook_data]
            );
        }
    }
    
    /**
     * Handle manual sync requests
     * 
     * @param array $sync_request
     */
    public function handle_manual_sync($sync_request)
    {
        try {
            $entity_type = $sync_request['entity_type'];
            $entity_ids = $sync_request['entity_ids'];
            $direction = $sync_request['direction'] ?? 'bidirectional';
            $force_update = $sync_request['force_update'] ?? false;
            
            foreach ($entity_ids as $entity_id) {
                $job_id = $this->queue_processor->add_to_queue(
                    $entity_type,
                    $entity_id,
                    $force_update ? 'update' : 'create',
                    $direction,
                    QueueProcessor::PRIORITY_HIGH,
                    [
                        'trigger' => 'manual_sync',
                        'force_update' => $force_update,
                        'requested_by' => get_staff_user_id()
                    ],
                    0 // No delay for manual sync
                );
                
                if ($job_id) {
                    log_activity("Manual sync requested: {$entity_type} #{$entity_id} (Job: {$job_id})");
                }
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'MANUAL_SYNC_HOOK_FAILED',
                $e->getMessage(),
                ['sync_request' => $sync_request]
            );
        }
    }
    
    /**
     * Check if entity type should be synced
     * 
     * @param string $entity_type
     * @return bool
     */
    protected function should_sync_entity($entity_type)
    {
        $sync_enabled = get_option('desk_moloni_sync_enabled') == '1';
        $entity_sync_enabled = get_option("desk_moloni_sync_{$entity_type}") == '1';
        
        return $sync_enabled && $entity_sync_enabled;
    }
    
    /**
     * Get sync priority for entity and action
     * 
     * @param string $entity_type
     * @param string $action
     * @return int
     */
    protected function get_sync_priority($entity_type, $action)
    {
        // High priority entities
        $high_priority_entities = ['invoice', 'payment'];
        
        if (in_array($entity_type, $high_priority_entities)) {
            return QueueProcessor::PRIORITY_HIGH;
        }
        
        // Critical actions
        if ($action === 'delete') {
            return QueueProcessor::PRIORITY_HIGH;
        }
        
        return QueueProcessor::PRIORITY_NORMAL;
    }
    
    /**
     * Get sync delay for entity and action
     * 
     * @param string $entity_type
     * @param string $action
     * @return int
     */
    protected function get_sync_delay($entity_type, $action)
    {
        $default_delay = (int)get_option('desk_moloni_auto_sync_delay', self::DEFAULT_SYNC_DELAY);
        
        // No delay for critical actions
        if ($action === 'delete') {
            return 0;
        }
        
        // Reduced delay for important entities
        $important_entities = ['invoice', 'payment'];
        if (in_array($entity_type, $important_entities)) {
            return min($default_delay, self::CRITICAL_SYNC_DELAY);
        }
        
        return $default_delay;
    }
    
    /**
     * Check if data changes are significant enough to trigger sync
     * 
     * @param string $entity_type
     * @param array $changed_data
     * @return bool
     */
    protected function has_significant_changes($entity_type, $changed_data)
    {
        $significant_fields = $this->get_significant_fields($entity_type);
        
        foreach (array_keys($changed_data) as $field) {
            if (in_array($field, $significant_fields)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Get significant fields for entity type
     * 
     * @param string $entity_type
     * @return array
     */
    protected function get_significant_fields($entity_type)
    {
        $field_mappings = [
            'customer' => ['company', 'vat', 'email', 'phonenumber', 'billing_street', 'billing_city', 'billing_zip'],
            'product' => ['description', 'rate', 'tax', 'unit'],
            'invoice' => ['total', 'subtotal', 'tax', 'status', 'date', 'duedate'],
            'estimate' => ['total', 'subtotal', 'tax', 'status', 'date', 'expirydate']
        ];
        
        return $field_mappings[$entity_type] ?? [];
    }
    
    /**
     * Ensure client is synced for invoice
     * 
     * @param int $invoice_id
     */
    protected function ensure_client_synced_for_invoice($invoice_id)
    {
        try {
            $this->CI->load->model('invoices_model');
            $invoice = $this->CI->invoices_model->get($invoice_id);
            
            if (!$invoice) {
                return;
            }
            
            $client_mapping = $this->entity_mapping->get_mapping_by_perfex_id(
                EntityMappingService::ENTITY_CUSTOMER,
                $invoice->clientid
            );
            
            if (!$client_mapping) {
                // Client not synced, add to queue
                $this->queue_processor->add_to_queue(
                    EntityMappingService::ENTITY_CUSTOMER,
                    $invoice->clientid,
                    'create',
                    'perfex_to_moloni',
                    QueueProcessor::PRIORITY_HIGH,
                    ['trigger' => 'invoice_client_dependency'],
                    0
                );
                
                log_activity("Client #{$invoice->clientid} queued for sync (dependency for invoice #{$invoice_id})");
            }
            
        } catch (\Exception $e) {
            $this->error_handler->log_error(
                ErrorHandler::CATEGORY_SYNC,
                'CLIENT_DEPENDENCY_SYNC_FAILED',
                $e->getMessage(),
                ['invoice_id' => $invoice_id]
            );
        }
    }
    
    /**
     * Get invoice update priority based on status and changes
     * 
     * @param object $invoice
     * @param array $data
     * @return int
     */
    protected function get_invoice_update_priority($invoice, $data)
    {
        // High priority for sent, paid, or cancelled invoices
        $high_priority_statuses = [2, 3, 5]; // Sent, Paid, Cancelled
        
        if (in_array($invoice->status, $high_priority_statuses)) {
            return QueueProcessor::PRIORITY_HIGH;
        }
        
        // High priority for financial changes
        $financial_fields = ['total', 'subtotal', 'tax', 'discount_total'];
        
        foreach ($financial_fields as $field) {
            if (array_key_exists($field, $data)) {
                return QueueProcessor::PRIORITY_HIGH;
            }
        }
        
        return QueueProcessor::PRIORITY_NORMAL;
    }
    
    /**
     * Get webhook priority based on entity and action
     * 
     * @param string $entity_type
     * @param string $action
     * @return int
     */
    protected function get_webhook_priority($entity_type, $action)
    {
        // Critical for financial documents
        $critical_entities = ['invoice', 'receipt', 'credit_note'];
        
        if (in_array($entity_type, $critical_entities)) {
            return QueueProcessor::PRIORITY_CRITICAL;
        }
        
        return QueueProcessor::PRIORITY_HIGH;
    }
    
    /**
     * Get hook statistics for monitoring
     * 
     * @return array
     */
    public function get_hook_statistics()
    {
        return [
            'total_hooks_triggered' => $this->model->count_hook_triggers(),
            'hooks_by_entity' => $this->model->count_hooks_by_entity(),
            'hooks_by_action' => $this->model->count_hooks_by_action(),
            'recent_hooks' => $this->model->get_recent_hook_triggers(10),
            'failed_hooks' => $this->model->get_failed_hook_triggers(10)
        ];
    }
}