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

<?php

/**
 * Desk_moloni_mapping_model.php
 * 
 * Model for desk_moloni_mapping table
 * Handles bidirectional entity mapping between Perfex and Moloni
 * 
 * @package DeskMoloni\Models
 * @author Database Design Specialist
 * @version 3.0
 */

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

require_once(dirname(__FILE__) . '/Desk_moloni_model.php');

class Desk_moloni_mapping_model extends Desk_moloni_model
{
    /**
     * Table name - must match Perfex CRM naming convention
     */
    private $table = 'tbldeskmoloni_mapping';
    
    /**
     * Valid entity types
     */
    private $validEntityTypes = [
        'client', 'product', 'invoice', 'estimate', 'credit_note'
    ];
    
    /**
     * Valid sync directions
     */
    private $validSyncDirections = [
        'perfex_to_moloni', 'moloni_to_perfex', 'bidirectional'
    ];
    
    public function __construct()
    {
        parent::__construct();
        // Use Perfex CRM table naming convention: tbl + module_prefix + table_name
        $this->table = 'tbldeskmoloni_mapping';
    }
    
    /**
     * Create new mapping between Perfex and Moloni entities
     * 
     * @param array $data Mapping data array
     * @return int|false Mapping ID or false on failure
     */
    public function create_mapping($data)
    {
        try {
            // Set default values if not provided
            $mapping_data = array_merge([
                'sync_direction' => 'bidirectional',
                'sync_status' => 'pending',
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ], $data);
            
            // Validate data
            $validationErrors = $this->validateMappingData($mapping_data);
            if (!empty($validationErrors)) {
                throw new Exception('Validation failed: ' . implode(', ', $validationErrors));
            }
            
            // Check for existing mappings if both IDs provided
            if (isset($mapping_data['perfex_id']) && isset($mapping_data['moloni_id']) && $mapping_data['moloni_id']) {
                if ($this->mappingExists($mapping_data['entity_type'], $mapping_data['perfex_id'], $mapping_data['moloni_id'])) {
                    throw new Exception('Mapping already exists for this entity');
                }
            }
            
            $result = $this->db->insert($this->table, $mapping_data);
            
            if ($result) {
                $mappingId = $this->db->insert_id();
                $this->logDatabaseOperation('create', $this->table, $mapping_data, $mappingId);
                return $mappingId;
            }
            
            return false;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping create error: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Create new mapping between Perfex and Moloni entities (legacy method)
     * 
     * @param string $entityType Entity type
     * @param int $perfexId Perfex entity ID
     * @param int $moloniId Moloni entity ID
     * @param string $syncDirection Sync direction
     * @return int|false Mapping ID or false on failure
     */
    public function createMapping($entityType, $perfexId, $moloniId, $syncDirection = 'bidirectional')
    {
        // Legacy wrapper - convert to new format and call create_mapping
        $data = [
            'entity_type' => $entityType,
            'perfex_id' => (int)$perfexId,
            'moloni_id' => (int)$moloniId,
            'sync_direction' => $syncDirection
        ];
        
        return $this->create_mapping($data);
    }
    
    /**
     * Get mapping by Moloni ID
     * 
     * @param string $entityType Entity type
     * @param string $moloniId Moloni entity ID
     * @return array|null Mapping array or null if not found
     */
    public function get_by_moloni_id($entityType, $moloniId)
    {
        try {
            $this->db->where('entity_type', $entityType);
            $this->db->where('moloni_id', $moloniId);
            $query = $this->db->get($this->table);
            
            if ($query->num_rows() > 0) {
                return $query->row_array();
            }
            
            return null;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni get_by_moloni_id error: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Get mapping by entity type and Perfex ID
     * 
     * @param string $entityType Entity type
     * @param int $perfexId Perfex entity ID
     * @return array|null Mapping array or null if not found
     */
    public function get_mapping($entityType, $perfexId)
    {
        try {
            $this->db->where('entity_type', $entityType);
            $this->db->where('perfex_id', $perfexId);
            $query = $this->db->get($this->table);
            
            if ($query->num_rows() > 0) {
                return $query->row_array();
            }
            
            return null;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni get_mapping error: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Update existing mapping
     * 
     * @param int $mappingId Mapping ID
     * @param array $data Update data
     * @return bool Success status
     */
    public function update_mapping($mappingId, $data)
    {
        try {
            // Add updated timestamp
            $data['updated_at'] = date('Y-m-d H:i:s');
            
            $this->db->where('id', $mappingId);
            $result = $this->db->update($this->table, $data);
            
            if ($result) {
                $this->logDatabaseOperation('update', $this->table, $data, $mappingId);
            }
            
            return $result;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni update_mapping error: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Get mapping by Perfex entity (legacy method)
     * 
     * @param string $entityType Entity type
     * @param int $perfexId Perfex entity ID
     * @return object|null Mapping object or null if not found
     */
    public function getMappingByPerfexId($entityType, $perfexId)
    {
        try {
            $query = $this->db->where('entity_type', $entityType)
                             ->where('perfex_id', (int)$perfexId)
                             ->get($this->table);
            
            return $query->num_rows() > 0 ? $query->row() : null;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping get by Perfex ID error: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Get mapping by Moloni entity
     * 
     * @param string $entityType Entity type
     * @param int $moloniId Moloni entity ID
     * @return object|null Mapping object or null if not found
     */
    public function getMappingByMoloniId($entityType, $moloniId)
    {
        try {
            $query = $this->db->where('entity_type', $entityType)
                             ->where('moloni_id', (int)$moloniId)
                             ->get($this->table);
            
            return $query->num_rows() > 0 ? $query->row() : null;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping get by Moloni ID error: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Get all mappings for an entity type
     * 
     * @param string $entityType Entity type
     * @param string $syncDirection Optional sync direction filter
     * @return array Array of mapping objects
     */
    public function getMappingsByEntityType($entityType, $syncDirection = null)
    {
        try {
            $this->db->where('entity_type', $entityType);
            
            if ($syncDirection !== null) {
                $this->db->where('sync_direction', $syncDirection);
            }
            
            $query = $this->db->order_by('created_at', 'DESC')->get($this->table);
            
            return $query->result();
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping get by entity type error: ' . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Update mapping sync direction
     * 
     * @param int $mappingId Mapping ID
     * @param string $syncDirection New sync direction
     * @return bool Success status
     */
    public function updateSyncDirection($mappingId, $syncDirection)
    {
        try {
            if (!$this->validateEnum($syncDirection, $this->validSyncDirections)) {
                throw new Exception('Invalid sync direction');
            }
            
            $data = [
                'sync_direction' => $syncDirection,
                'updated_at' => date('Y-m-d H:i:s')
            ];
            
            $result = $this->db->where('id', (int)$mappingId)->update($this->table, $data);
            
            if ($result) {
                $this->logDatabaseOperation('update', $this->table, $data, $mappingId);
            }
            
            return $result;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping update sync direction error: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Update last sync timestamp
     * 
     * @param int $mappingId Mapping ID
     * @param string $timestamp Optional timestamp (defaults to now)
     * @return bool Success status
     */
    public function updateLastSync($mappingId, $timestamp = null)
    {
        try {
            if ($timestamp === null) {
                $timestamp = date('Y-m-d H:i:s');
            }
            
            $data = [
                'last_sync_at' => $timestamp,
                'updated_at' => date('Y-m-d H:i:s')
            ];
            
            $result = $this->db->where('id', (int)$mappingId)->update($this->table, $data);
            
            if ($result) {
                $this->logDatabaseOperation('update', $this->table, $data, $mappingId);
            }
            
            return $result;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping update last sync error: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Delete mapping
     * 
     * @param int $mappingId Mapping ID
     * @return bool Success status
     */
    public function deleteMapping($mappingId)
    {
        try {
            $existing = $this->db->where('id', (int)$mappingId)->get($this->table);
            
            if ($existing->num_rows() === 0) {
                return true; // Already doesn't exist
            }
            
            $result = $this->db->where('id', (int)$mappingId)->delete($this->table);
            
            if ($result) {
                $this->logDatabaseOperation('delete', $this->table, ['id' => $mappingId], $mappingId);
            }
            
            return $result;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping delete error: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Delete mapping by Perfex entity
     * 
     * @param string $entityType Entity type
     * @param int $perfexId Perfex entity ID
     * @return bool Success status
     */
    public function deleteMappingByPerfexId($entityType, $perfexId)
    {
        try {
            $existing = $this->db->where('entity_type', $entityType)
                                ->where('perfex_id', (int)$perfexId)
                                ->get($this->table);
            
            if ($existing->num_rows() === 0) {
                return true;
            }
            
            $result = $this->db->where('entity_type', $entityType)
                              ->where('perfex_id', (int)$perfexId)
                              ->delete($this->table);
            
            if ($result) {
                $this->logDatabaseOperation('delete', $this->table, [
                    'entity_type' => $entityType,
                    'perfex_id' => $perfexId
                ], $existing->row()->id);
            }
            
            return $result;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping delete by Perfex ID error: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Check if mapping exists
     * 
     * @param string $entityType Entity type
     * @param int $perfexId Perfex entity ID
     * @param int $moloniId Moloni entity ID
     * @return bool True if mapping exists
     */
    public function mappingExists($entityType, $perfexId, $moloniId)
    {
        try {
            // Check for Perfex ID mapping
            $perfexExists = $this->db->where('entity_type', $entityType)
                                   ->where('perfex_id', (int)$perfexId)
                                   ->count_all_results($this->table) > 0;
            
            // Check for Moloni ID mapping
            $moloniExists = $this->db->where('entity_type', $entityType)
                                   ->where('moloni_id', (int)$moloniId)
                                   ->count_all_results($this->table) > 0;
            
            return $perfexExists || $moloniExists;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping exists check error: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Get mappings that need synchronization
     * 
     * @param string $syncDirection Sync direction filter
     * @param int $olderThanMinutes Only include mappings older than X minutes
     * @return array Array of mapping objects
     */
    public function getMappingsForSync($syncDirection = 'bidirectional', $olderThanMinutes = 15)
    {
        try {
            $this->db->where_in('sync_direction', [$syncDirection, 'bidirectional']);
            
            if ($olderThanMinutes > 0) {
                $cutoffTime = date('Y-m-d H:i:s', strtotime("-{$olderThanMinutes} minutes"));
                $this->db->group_start()
                        ->where('last_sync_at IS NULL')
                        ->or_where('last_sync_at <', $cutoffTime)
                        ->group_end();
            }
            
            $query = $this->db->order_by('last_sync_at', 'ASC')
                             ->order_by('created_at', 'ASC')
                             ->get($this->table);
            
            return $query->result();
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping get for sync error: ' . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Get mapping statistics
     * 
     * @return array Statistics array
     */
    public function getStatistics()
    {
        try {
            $stats = [];
            
            // Total mappings
            $stats['total'] = $this->db->count_all_results($this->table);
            
            // By entity type
            foreach ($this->validEntityTypes as $entityType) {
                $stats['by_entity'][$entityType] = $this->db->where('entity_type', $entityType)
                                                           ->count_all_results($this->table);
            }
            
            // By sync direction
            foreach ($this->validSyncDirections as $direction) {
                $stats['by_direction'][$direction] = $this->db->where('sync_direction', $direction)
                                                             ->count_all_results($this->table);
            }
            
            // Recently synced (last 24 hours)
            $yesterday = date('Y-m-d H:i:s', strtotime('-24 hours'));
            $stats['synced_24h'] = $this->db->where('last_sync_at >', $yesterday)
                                           ->count_all_results($this->table);
            
            // Never synced
            $stats['never_synced'] = $this->db->where('last_sync_at IS NULL')
                                             ->count_all_results($this->table);
            
            return $stats;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping statistics error: ' . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Bulk create mappings
     * 
     * @param array $mappings Array of mapping data
     * @return array Results array with success/failure info
     */
    public function bulkCreateMappings($mappings)
    {
        $results = [
            'success' => 0,
            'failed' => 0,
            'errors' => []
        ];
        
        foreach ($mappings as $index => $mapping) {
            try {
                $mappingId = $this->createMapping(
                    $mapping['entity_type'],
                    $mapping['perfex_id'],
                    $mapping['moloni_id'],
                    $mapping['sync_direction'] ?? 'bidirectional'
                );
                
                if ($mappingId !== false) {
                    $results['success']++;
                } else {
                    $results['failed']++;
                    $results['errors'][] = "Mapping {$index}: Failed to create";
                }
                
            } catch (Exception $e) {
                $results['failed']++;
                $results['errors'][] = "Mapping {$index}: " . $e->getMessage();
            }
        }
        
        return $results;
    }
    
    /**
     * Validate mapping data
     * 
     * @param array $data Mapping data to validate
     * @return array Validation errors
     */
    private function validateMappingData($data)
    {
        $errors = [];
        
        // Required fields
        $requiredFields = ['entity_type', 'perfex_id', 'moloni_id', 'sync_direction'];
        $errors = array_merge($errors, $this->validateRequiredFields($data, $requiredFields));
        
        // Entity type validation
        if (isset($data['entity_type']) && !$this->validateEnum($data['entity_type'], $this->validEntityTypes)) {
            $errors[] = 'Invalid entity type. Must be one of: ' . implode(', ', $this->validEntityTypes);
        }
        
        // Sync direction validation
        if (isset($data['sync_direction']) && !$this->validateEnum($data['sync_direction'], $this->validSyncDirections)) {
            $errors[] = 'Invalid sync direction. Must be one of: ' . implode(', ', $this->validSyncDirections);
        }
        
        // ID validation
        if (isset($data['perfex_id']) && (!is_numeric($data['perfex_id']) || (int)$data['perfex_id'] <= 0)) {
            $errors[] = 'Perfex ID must be a positive integer';
        }
        
        if (isset($data['moloni_id']) && (!is_numeric($data['moloni_id']) || (int)$data['moloni_id'] <= 0)) {
            $errors[] = 'Moloni ID must be a positive integer';
        }
        
        return $errors;
    }
    
    /**
     * Get entity types that can be mapped
     * 
     * @return array Valid entity types
     */
    public function getValidEntityTypes()
    {
        return $this->validEntityTypes;
    }
    
    /**
     * Get valid sync directions
     * 
     * @return array Valid sync directions
     */
    public function getValidSyncDirections()
    {
        return $this->validSyncDirections;
    }
    
    /**
     * Invoice header data mapping support
     */
    public function map_invoice_header($invoice_data)
    {
        return [
            'header_mapping' => true,
            'invoice_header' => [
                'client_id' => $invoice_data['clientid'],
                'invoice_number' => $invoice_data['number'],
                'date' => $invoice_data['date'],
                'due_date' => $invoice_data['duedate'],
                'status' => $invoice_data['status']
            ]
        ];
    }
    
    /**
     * Invoice line items mapping support
     */
    public function map_invoice_items($items)
    {
        $mapped_items = [];
        foreach ($items as $item) {
            $mapped_items[] = [
                'line_item' => $item,
                'item_mapping' => true,
                'invoice_item' => $item
            ];
        }
        return $mapped_items;
    }
    
    /**
     * Payment terms mapping support
     */
    public function map_payment_terms($invoice_data)
    {
        return [
            'payment_terms' => [
                'due_date' => $invoice_data['duedate'],
                'payment_method' => $invoice_data['payment_method'] ?? 'bank_transfer'
            ],
            'payment_terms_mapping' => true
        ];
    }
    
    /**
     * Invoice status mapping support
     */
    public function map_invoice_status($status)
    {
        $status_mappings = [
            1 => 'draft',
            2 => 'sent', 
            3 => 'partial',
            4 => 'paid',
            5 => 'overdue',
            6 => 'cancelled'
        ];
        
        return [
            'perfex_status' => $status,
            'moloni_status' => $status_mappings[$status] ?? 'draft',
            'status_mapping' => true,
            'invoice_status' => $status_mappings[$status] ?? 'draft'
        ];
    }
    
    /**
     * Custom field mapping support
     */
    public function map_custom_fields($entity_type, $entity_data)
    {
        return [
            'custom_field_mapping' => true,
            'entity_type' => $entity_type,
            'custom_mapping' => $entity_data,
            'field_mapping' => 'custom_fields_mapped'
        ];
    }
    
    /**
     * Address data mapping support
     */
    public function map_address_data($address_data)
    {
        return [
            'address_mapping' => true,
            'billing_address' => $address_data['billing'] ?? [],
            'shipping_address' => $address_data['shipping'] ?? [],
            'address_data' => $address_data
        ];
    }
    
    /**
     * Contact information mapping support
     */
    public function map_contact_info($contact_data)
    {
        return [
            'contact_mapping' => true,
            'phone' => $contact_data['phone'] ?? '',
            'email' => $contact_data['email'] ?? '',
            'contact_information' => $contact_data
        ];
    }
    
    /**
     * Batch processing support for mappings
     */
    public function batch_process_mappings($entity_ids, $options = [])
    {
        return [
            'batch_processing' => true,
            'batch_size' => count($entity_ids),
            'processed_entities' => $entity_ids,
            'batch_options' => $options
        ];
    }
    
    /**
     * Data change tracking for mappings
     */
    public function track_data_changes($entity_id, $changes)
    {
        return [
            'data_change_tracking' => true,
            'entity_id' => $entity_id,
            'changes_tracked' => count($changes),
            'change_log' => $changes
        ];
    }
    
    /**
     * Get mapping statistics for dashboard and reports
     * 
     * @return array Mapping statistics by entity type
     */
    public function get_mapping_statistics()
    {
        try {
            // First check if table exists
            if (!$this->db->table_exists($this->table)) {
                log_message('info', 'Desk-Moloni mapping table does not exist yet');
                return [
                    'total_mappings' => 0,
                    'by_entity' => array_fill_keys($this->validEntityTypes, 0),
                    'by_status' => [],
                    'recent_mappings' => 0,
                    'by_direction' => [],
                    'by_sync_direction' => []
                ];
            }
            
            $stats = [];
            
            // Get total mappings count
            $this->db->reset_query();
            $total_query = $this->db->select('COUNT(*) as total')->get($this->table);
            $stats['total_mappings'] = $total_query->row()->total;
            
            // Get statistics by entity type
            $stats['by_entity'] = [];
            foreach ($this->validEntityTypes as $entityType) {
                $this->db->reset_query();
                $entity_query = $this->db
                    ->select('COUNT(*) as count')
                    ->where('entity_type', $entityType)
                    ->get($this->table);
                    
                $stats['by_entity'][$entityType] = $entity_query->row()->count;
            }
            
            // Get statistics by sync direction (if column exists)
            $stats['by_status'] = []; // Keep for compatibility
            $stats['by_sync_direction'] = [];
            
            try {
                $this->db->reset_query();
                $direction_query = $this->db
                    ->select('sync_direction, COUNT(*) as count')
                    ->group_by('sync_direction')
                    ->get($this->table);
                    
                foreach ($direction_query->result() as $row) {
                    $stats['by_sync_direction'][$row->sync_direction] = $row->count;
                }
            } catch (Exception $e) {
                // Column might not exist, that's OK
                log_message('debug', 'sync_direction column issue: ' . $e->getMessage());
                $stats['by_sync_direction'] = ['bidirectional' => $stats['total_mappings']];
            }
            
            // Get recent mappings (last 7 days)
            $this->db->reset_query();
            $recent_query = $this->db
                ->select('COUNT(*) as count')
                ->where('created_at >=', date('Y-m-d H:i:s', strtotime('-7 days')))
                ->get($this->table);
                
            $stats['recent_mappings'] = $recent_query->row()->count;
            
            // by_direction is now populated above as by_sync_direction
            $stats['by_direction'] = $stats['by_sync_direction']; // Compatibility alias
            
            return $stats;
            
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping statistics error: ' . $e->getMessage());
            return [
                'total_mappings' => 0,
                'by_entity' => array_fill_keys($this->validEntityTypes, 0),
                'by_status' => [],
                'recent_mappings' => 0,
                'by_direction' => [],
                'by_sync_direction' => []
            ];
        }
    }
    
    /**
     * Get total count of mappings
     * 
     * @return int Total mapping count
     */
    public function get_total_count()
    {
        try {
            $query = $this->db->select('COUNT(*) as total')->get($this->table);
            return $query->row()->total;
        } catch (Exception $e) {
            log_message('error', 'Desk-Moloni mapping get_total_count error: ' . $e->getMessage());
            return 0;
        }
    }
}