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

<?php

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

/**
 * Desk-Moloni Helper Functions
 * 
 * Collection of utility functions for the Desk-Moloni module
 * to simplify common operations and provide consistent interfaces.
 * 
 * @package    DeskMoloni
 * @subpackage Helpers
 * @version    3.0.0
 * @author     Descomplicar®
 */

if (!function_exists('desk_moloni_log')) {
    /**
     * Centralized logging function for Desk-Moloni
     * 
     * @param string $level Log level (error, info, debug, warning)
     * @param string $message Log message
     * @param array $context Additional context data
     * @param string $category Log category (api, sync, oauth, etc.)
     */
    function desk_moloni_log($level, $message, $context = [], $category = 'general')
    {
        $timestamp = date('Y-m-d H:i:s');
        $formatted_message = "[$timestamp] [DESK-MOLONI] [$category] [$level] $message";
        
        if (!empty($context)) {
            $formatted_message .= ' | Context: ' . json_encode($context, JSON_UNESCAPED_UNICODE);
        }
        
        // Log to CodeIgniter log
        log_message($level, $formatted_message);
        
        // Also log to custom desk_moloni log file if debug mode is enabled
        if (get_option('desk_moloni_debug_mode') == '1') {
            $log_dir = APPPATH . '../uploads/desk_moloni/logs/';
            if (!is_dir($log_dir)) {
                mkdir($log_dir, 0755, true);
            }
            
            $log_file = $log_dir . 'desk_moloni_' . date('Y-m-d') . '.log';
            file_put_contents($log_file, $formatted_message . PHP_EOL, FILE_APPEND | LOCK_EX);
        }
    }
}

if (!function_exists('desk_moloni_log_api')) {
    /**
     * Specialized logging for API calls
     */
    function desk_moloni_log_api($endpoint, $method, $data = [], $response = [], $execution_time = null)
    {
        $context = [
            'endpoint' => $endpoint,
            'method' => $method,
            'request_data' => $data,
            'response_data' => $response,
            'execution_time_ms' => $execution_time
        ];
        
        desk_moloni_log('info', "API Call: $method $endpoint", $context, 'api');
    }
}

if (!function_exists('desk_moloni_log_sync')) {
    /**
     * Specialized logging for sync operations
     */
    function desk_moloni_log_sync($entity_type, $entity_id, $action, $status, $details = [])
    {
        $context = [
            'entity_type' => $entity_type,
            'entity_id' => $entity_id,
            'action' => $action,
            'status' => $status,
            'details' => $details
        ];
        
        $level = ($status === 'success') ? 'info' : 'error';
        desk_moloni_log($level, "Sync $action for $entity_type #$entity_id: $status", $context, 'sync');
    }
}

if (!function_exists('desk_moloni_is_enabled')) {
    /**
     * Check if Desk-Moloni module is enabled
     * 
     * @return bool
     */
    function desk_moloni_is_enabled()
    {
        return get_option('desk_moloni_enabled') == '1';
    }
}

if (!function_exists('validate_moloni_data')) {
    /**
     * Centralized data validation for Moloni data
     * 
     * @param array $data Data to validate
     * @param array $rules Validation rules
     * @param array $messages Custom error messages
     * @return array ['valid' => bool, 'errors' => array]
     */
    function validate_moloni_data($data, $rules, $messages = [])
    {
        $CI = &get_instance();
        $CI->load->library('form_validation');
        
        // Clear previous rules
        $CI->form_validation->reset_validation();
        
        // Set validation rules
        foreach ($rules as $field => $rule) {
            $label = isset($messages[$field]) ? $messages[$field] : ucfirst(str_replace('_', ' ', $field));
            $CI->form_validation->set_rules($field, $label, $rule, $messages);
        }
        
        $is_valid = $CI->form_validation->run($data);
        
        $result = [
            'valid' => $is_valid,
            'errors' => []
        ];
        
        if (!$is_valid) {
            $result['errors'] = $CI->form_validation->error_array();
            desk_moloni_log('warning', 'Validation failed', [
                'data' => $data, 
                'errors' => $result['errors']
            ], 'validation');
        }
        
        return $result;
    }
}

if (!function_exists('validate_moloni_client')) {
    /**
     * Validate client data for Moloni
     */
    function validate_moloni_client($client_data)
    {
        $rules = [
            'name' => 'required|max_length[255]',
            'vat' => 'required|exact_length[9]|numeric',
            'email' => 'valid_email',
            'phone' => 'max_length[20]',
            'address' => 'max_length[255]',
            'city' => 'max_length[100]',
            'zip_code' => 'max_length[20]',
            'country_id' => 'required|numeric'
        ];
        
        $messages = [
            'name' => 'Client name',
            'vat' => 'VAT number',
            'email' => 'Email address',
            'phone' => 'Phone number',
            'address' => 'Address',
            'city' => 'City',
            'zip_code' => 'ZIP code',
            'country_id' => 'Country'
        ];
        
        return validate_moloni_data($client_data, $rules, $messages);
    }
}

if (!function_exists('validate_moloni_product')) {
    /**
     * Validate product data for Moloni
     */
    function validate_moloni_product($product_data)
    {
        $rules = [
            'name' => 'required|max_length[255]',
            'reference' => 'max_length[50]',
            'price' => 'required|numeric|greater_than[0]',
            'category_id' => 'numeric',
            'unit_id' => 'numeric',
            'tax_id' => 'required|numeric',
            'stock_enabled' => 'in_list[0,1]'
        ];
        
        $messages = [
            'name' => 'Product name',
            'reference' => 'Product reference',
            'price' => 'Product price',
            'category_id' => 'Category',
            'unit_id' => 'Unit',
            'tax_id' => 'Tax',
            'stock_enabled' => 'Stock control'
        ];
        
        return validate_moloni_data($product_data, $rules, $messages);
    }
}

if (!function_exists('validate_moloni_invoice')) {
    /**
     * Validate invoice data for Moloni
     */
    function validate_moloni_invoice($invoice_data)
    {
        $rules = [
            'customer_id' => 'required|numeric',
            'document_type' => 'required|in_list[invoices,receipts,bills_of_lading,estimates]',
            'products' => 'required|is_array',
            'date' => 'required|valid_date[Y-m-d]',
            'due_date' => 'valid_date[Y-m-d]',
            'notes' => 'max_length[500]'
        ];
        
        $messages = [
            'customer_id' => 'Customer',
            'document_type' => 'Document type',
            'products' => 'Products',
            'date' => 'Invoice date',
            'due_date' => 'Due date',
            'notes' => 'Notes'
        ];
        
        // Validate main invoice data
        $validation = validate_moloni_data($invoice_data, $rules, $messages);
        
        // Validate products if present
        if (isset($invoice_data['products']) && is_array($invoice_data['products'])) {
            foreach ($invoice_data['products'] as $index => $product) {
                $product_rules = [
                    'product_id' => 'required|numeric',
                    'qty' => 'required|numeric|greater_than[0]',
                    'price' => 'required|numeric|greater_than_equal_to[0]'
                ];
                
                $product_validation = validate_moloni_data($product, $product_rules);
                if (!$product_validation['valid']) {
                    $validation['valid'] = false;
                    foreach ($product_validation['errors'] as $field => $error) {
                        $validation['errors']["products[{$index}][{$field}]"] = $error;
                    }
                }
            }
        }
        
        return $validation;
    }
}

if (!function_exists('validate_moloni_api_response')) {
    /**
     * Validate Moloni API response
     */
    function validate_moloni_api_response($response)
    {
        if (!is_array($response)) {
            return [
                'valid' => false,
                'errors' => ['Invalid response format']
            ];
        }
        
        // Check for API errors
        if (isset($response['error'])) {
            return [
                'valid' => false,
                'errors' => [$response['error']]
            ];
        }
        
        return [
            'valid' => true,
            'errors' => []
        ];
    }
}

if (!function_exists('sanitize_moloni_data')) {
    /**
     * Sanitize data for Moloni API
     */
    function sanitize_moloni_data($data)
    {
        if (is_array($data)) {
            $sanitized = [];
            foreach ($data as $key => $value) {
                $sanitized[$key] = sanitize_moloni_data($value);
            }
            return $sanitized;
        }
        
        if (is_string($data)) {
            // Remove harmful characters, trim whitespace
            $data = trim($data);
            $data = filter_var($data, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
            return $data;
        }
        
        return $data;
    }
}

if (!function_exists('verify_desk_moloni_csrf')) {
    /**
     * Verify CSRF token for Desk-Moloni forms
     * 
     * @param bool $ajax_response Return JSON response for AJAX requests
     * @return bool|void True if valid, false or exit if invalid
     */
    function verify_desk_moloni_csrf($ajax_response = false)
    {
        $CI = &get_instance();
        
        // Check if CSRF verification is enabled
        if ($CI->config->item('csrf_protection') !== TRUE) {
            return true;
        }
        
        $token_name = $CI->security->get_csrf_token_name();
        $posted_token = $CI->input->post($token_name);
        $session_token = $CI->security->get_csrf_hash();
        
        if (!$posted_token || !hash_equals($session_token, $posted_token)) {
            desk_moloni_log('warning', 'CSRF token validation failed', [
                'ip' => $CI->input->ip_address(),
                'user_agent' => $CI->input->user_agent(),
                'posted_token' => $posted_token ? 'present' : 'missing',
                'session_token' => $session_token ? 'present' : 'missing'
            ], 'security');
            
            if ($ajax_response) {
                header('Content-Type: application/json');
                echo json_encode([
                    'success' => false,
                    'error' => 'Invalid security token. Please refresh the page and try again.',
                    'csrf_error' => true
                ]);
                exit;
            } else {
                show_error('Invalid security token. Please refresh the page and try again.', 403, 'Security Error');
                return false;
            }
        }
        
        return true;
    }
}

if (!function_exists('get_desk_moloni_csrf_data')) {
    /**
     * Get CSRF data for JavaScript/AJAX use
     * 
     * @return array CSRF token name and value
     */
    function get_desk_moloni_csrf_data()
    {
        $CI = &get_instance();
        
        return [
            'name' => $CI->security->get_csrf_token_name(),
            'value' => $CI->security->get_csrf_hash()
        ];
    }
}

if (!function_exists('include_csrf_protection')) {
    /**
     * Include CSRF protection in forms - shortcut function
     */
    function include_csrf_protection()
    {
        $CI = &get_instance();
        $token_name = $CI->security->get_csrf_token_name();
        $token_value = $CI->security->get_csrf_hash();
        
        echo '<input type="hidden" name="' . $token_name . '" value="' . $token_value . '">';
    }
}

if (!function_exists('desk_moloni_get_api_client')) {
    /**
     * Get configured API client instance
     * 
     * @return object|null
     */
    function desk_moloni_get_api_client()
    {
        $CI = &get_instance();
        $CI->load->library('desk_moloni/moloni_api_client');
        return $CI->moloni_api_client;
    }
}

if (!function_exists('desk_moloni_format_currency')) {
    /**
     * Format currency value for Moloni API
     * 
     * @param float $amount
     * @param string $currency
     * @return string
     */
    function desk_moloni_format_currency($amount, $currency = 'EUR')
    {
        return number_format((float)$amount, 2, '.', '');
    }
}

if (!function_exists('desk_moloni_validate_vat')) {
    /**
     * Validate VAT number format
     * 
     * @param string $vat
     * @param string $country_code
     * @return bool
     */
    function desk_moloni_validate_vat($vat, $country_code = 'PT')
    {
        $vat = preg_replace('/[^0-9A-Za-z]/', '', $vat);
        
        switch (strtoupper($country_code)) {
            case 'PT':
                return preg_match('/^[0-9]{9}$/', $vat);
            case 'ES':
                return preg_match('/^[A-Z0-9][0-9]{7}[A-Z0-9]$/', $vat);
            default:
                return strlen($vat) >= 8 && strlen($vat) <= 12;
        }
    }
}

if (!function_exists('desk_moloni_get_sync_status')) {
    /**
     * Get synchronization status for an entity
     * 
     * @param string $entity_type
     * @param int $entity_id
     * @return string|null
     */
    function desk_moloni_get_sync_status($entity_type, $entity_id)
    {
        $CI = &get_instance();
        
        $mapping = $CI->db->get_where(db_prefix() . 'desk_moloni_entity_mappings', [
            'entity_type' => $entity_type,
            'perfex_id' => $entity_id
        ])->row();
        
        return $mapping ? $mapping->sync_status : null;
    }
}

if (!function_exists('desk_moloni_queue_sync')) {
    /**
     * Queue an entity for synchronization
     * 
     * @param string $entity_type
     * @param int $entity_id
     * @param string $action
     * @param string $priority
     * @return bool
     */
    function desk_moloni_queue_sync($entity_type, $entity_id, $action = 'sync', $priority = 'normal')
    {
        $CI = &get_instance();
        
        $queue_data = [
            'entity_type' => $entity_type,
            'entity_id' => $entity_id,
            'action' => $action,
            'priority' => $priority,
            'status' => 'pending',
            'created_at' => date('Y-m-d H:i:s'),
            'created_by' => get_staff_user_id()
        ];
        
        return $CI->db->insert(db_prefix() . 'desk_moloni_sync_queue', $queue_data);
    }
}

if (!function_exists('desk_moloni_log_error')) {
    /**
     * Log an error to the error logs table
     * 
     * @param string $error_type
     * @param string $message
     * @param array $context
     * @param string $severity
     * @return bool
     */
    function desk_moloni_log_error($error_type, $message, $context = [], $severity = 'medium')
    {
        $CI = &get_instance();
        
        $error_data = [
            'error_type' => $error_type,
            'severity' => $severity,
            'message' => $message,
            'context' => !empty($context) ? json_encode($context) : null,
            'created_at' => date('Y-m-d H:i:s'),
            'created_by' => get_staff_user_id(),
            'ip_address' => $CI->input->ip_address()
        ];
        
        return $CI->db->insert(db_prefix() . 'desk_moloni_error_logs', $error_data);
    }
}

if (!function_exists('desk_moloni_encrypt_data')) {
    /**
     * Encrypt sensitive data
     * 
     * @param mixed $data
     * @param string $context
     * @return string|false
     */
    function desk_moloni_encrypt_data($data, $context = '')
    {
        if (!get_option('desk_moloni_enable_encryption')) {
            return $data;
        }
        
        $CI = &get_instance();
        $CI->load->library('desk_moloni/Encryption');
        
        try {
            return $CI->encryption->encrypt($data, $context);
        } catch (Exception $e) {
            desk_moloni_log_error('encryption', 'Failed to encrypt data: ' . $e->getMessage());
            return false;
        }
    }
}

if (!function_exists('desk_moloni_decrypt_data')) {
    /**
     * Decrypt sensitive data
     * 
     * @param string $encrypted_data
     * @param string $context
     * @return mixed|false
     */
    function desk_moloni_decrypt_data($encrypted_data, $context = '')
    {
        if (!get_option('desk_moloni_enable_encryption')) {
            return $encrypted_data;
        }
        
        $CI = &get_instance();
        $CI->load->library('desk_moloni/Encryption');
        
        try {
            return $CI->encryption->decrypt($encrypted_data, $context);
        } catch (Exception $e) {
            desk_moloni_log_error('decryption', 'Failed to decrypt data: ' . $e->getMessage());
            return false;
        }
    }
}

if (!function_exists('desk_moloni_get_performance_metrics')) {
    /**
     * Get performance metrics for a time period
     * 
     * @param string $metric_type
     * @param string $start_date
     * @param string $end_date
     * @return array
     */
    function desk_moloni_get_performance_metrics($metric_type = null, $start_date = null, $end_date = null)
    {
        $CI = &get_instance();
        
        $CI->db->select('metric_name, AVG(metric_value) as avg_value, MAX(metric_value) as max_value, MIN(metric_value) as min_value, COUNT(*) as count')
               ->from(db_prefix() . 'desk_moloni_performance_metrics');
        
        if ($metric_type) {
            $CI->db->where('metric_type', $metric_type);
        }
        
        if ($start_date) {
            $CI->db->where('recorded_at >=', $start_date);
        }
        
        if ($end_date) {
            $CI->db->where('recorded_at <=', $end_date);
        }
        
        $CI->db->group_by('metric_name');
        
        return $CI->db->get()->result_array();
    }
}

if (!function_exists('desk_moloni_clean_phone_number')) {
    /**
     * Clean and format phone number
     * 
     * @param string $phone
     * @return string
     */
    function desk_moloni_clean_phone_number($phone)
    {
        // Remove all non-digit characters except +
        $cleaned = preg_replace('/[^+\d]/', '', $phone);
        
        // If starts with 00, replace with +
        if (substr($cleaned, 0, 2) === '00') {
            $cleaned = '+' . substr($cleaned, 2);
        }
        
        // If Portuguese number without country code, add it
        if (preg_match('/^[29]\d{8}$/', $cleaned)) {
            $cleaned = '+351' . $cleaned;
        }
        
        return $cleaned;
    }
}

if (!function_exists('desk_moloni_sanitize_html')) {
    /**
     * Sanitize HTML content for safe storage
     * 
     * @param string $html
     * @return string
     */
    function desk_moloni_sanitize_html($html)
    {
        $allowed_tags = '<p><br><strong><b><em><i><u><ul><ol><li><a>';
        return strip_tags($html, $allowed_tags);
    }
}

if (!function_exists('desk_moloni_generate_reference')) {
    /**
     * Generate a unique reference for sync operations
     * 
     * @param string $prefix
     * @return string
     */
    function desk_moloni_generate_reference($prefix = 'DM')
    {
        return $prefix . date('YmdHis') . sprintf('%04d', mt_rand(0, 9999));
    }
}

if (!function_exists('desk_moloni_is_debug_mode')) {
    /**
     * Check if debug mode is enabled
     * 
     * @return bool
     */
    function desk_moloni_is_debug_mode()
    {
        return get_option('desk_moloni_debug_mode') == '1' || ENVIRONMENT === 'development';
    }
}

if (!function_exists('desk_moloni_cache_key')) {
    /**
     * Generate a cache key for the given parameters
     * 
     * @param string $type
     * @param mixed ...$params
     * @return string
     */
    function desk_moloni_cache_key($type, ...$params)
    {
        $key_parts = [$type];
        foreach ($params as $param) {
            $key_parts[] = is_array($param) ? md5(serialize($param)) : (string)$param;
        }
        return 'desk_moloni:' . implode(':', $key_parts);
    }
}

if (!function_exists('desk_moloni_get_cached_data')) {
    /**
     * Get data from cache
     * 
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
    function desk_moloni_get_cached_data($key, $default = null)
    {
        if (!get_option('desk_moloni_enable_caching')) {
            return $default;
        }
        
        $CI = &get_instance();
        
        // Try to get from Redis if available
        if (get_option('desk_moloni_enable_redis')) {
            $CI->load->library('redis');
            $data = $CI->redis->get($key);
            return $data !== null ? json_decode($data, true) : $default;
        }
        
        // Fallback to file cache
        $cache_file = DESK_MOLONI_MODULE_UPLOAD_FOLDER . 'cache/' . md5($key) . '.cache';
        if (file_exists($cache_file)) {
            $cache_data = json_decode(file_get_contents($cache_file), true);
            if ($cache_data && $cache_data['expires'] > time()) {
                return $cache_data['data'];
            }
        }
        
        return $default;
    }
}

if (!function_exists('desk_moloni_set_cached_data')) {
    /**
     * Set data in cache
     * 
     * @param string $key
     * @param mixed $data
     * @param int $ttl
     * @return bool
     */
    function desk_moloni_set_cached_data($key, $data, $ttl = 3600)
    {
        if (!get_option('desk_moloni_enable_caching')) {
            return false;
        }
        
        $CI = &get_instance();
        
        // Try to set in Redis if available
        if (get_option('desk_moloni_enable_redis')) {
            $CI->load->library('redis');
            return $CI->redis->setex($key, $ttl, json_encode($data));
        }
        
        // Fallback to file cache
        $cache_dir = DESK_MOLONI_MODULE_UPLOAD_FOLDER . 'cache/';
        if (!is_dir($cache_dir)) {
            mkdir($cache_dir, 0755, true);
        }
        
        $cache_file = $cache_dir . md5($key) . '.cache';
        $cache_data = [
            'data' => $data,
            'expires' => time() + $ttl,
            'created' => time()
        ];
        
        return file_put_contents($cache_file, json_encode($cache_data)) !== false;
    }
}

if (!function_exists('desk_moloni_format_date')) {
    /**
     * Format date for Moloni API
     * 
     * @param string $date
     * @param string $format
     * @return string
     */
    function desk_moloni_format_date($date, $format = 'Y-m-d')
    {
        if (empty($date)) {
            return '';
        }
        
        $timestamp = is_numeric($date) ? $date : strtotime($date);
        return date($format, $timestamp);
    }
}

if (!function_exists('desk_moloni_get_module_version')) {
    /**
     * Get module version
     * 
     * @return string
     */
    function desk_moloni_get_module_version()
    {
        return defined('DESK_MOLONI_MODULE_VERSION') ? DESK_MOLONI_MODULE_VERSION : DESK_MOLONI_VERSION;
    }
}

if (!function_exists('desk_moloni_has_permission')) {
    /**
     * Check if current user has permission for Desk-Moloni operations
     * 
     * @param string $capability
     * @return bool
     */
    function desk_moloni_has_permission($capability = 'view')
    {
        return has_permission('desk_moloni', '', $capability);
    }
}

if (!function_exists('desk_moloni_admin_url')) {
    /**
     * Generate admin URL for Desk-Moloni module
     * 
     * @param string $path
     * @return string
     */
    function desk_moloni_admin_url($path = '')
    {
        return admin_url('desk_moloni' . ($path ? '/' . ltrim($path, '/') : ''));
    }
}