<?php

namespace DeskMoloni\Libraries;

/**
 * Descomplicar® Crescimento Digital
 * https://descomplicar.pt
 * 
 * Entity Mapping Service
 * Handles mapping and relationship management between Perfex CRM and Moloni ERP entities
 * 
 * @package DeskMoloni
 * @subpackage Libraries
 * @category EntityMapping
 * @author Descomplicar® - PHP Fullstack Engineer
 * @version 1.0.0
 */

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

class EntityMappingService
{
    protected $CI;
    protected $model;
    
    // Entity types supported
    const ENTITY_CUSTOMER = 'customer';
    const ENTITY_PRODUCT = 'product';
    const ENTITY_INVOICE = 'invoice';
    const ENTITY_ESTIMATE = 'estimate';
    const ENTITY_CREDIT_NOTE = 'credit_note';
    
    // Mapping status constants
    const STATUS_PENDING = 'pending';
    const STATUS_SYNCED = 'synced';
    const STATUS_ERROR = 'error';
    const STATUS_CONFLICT = 'conflict';
    
    // Sync directions
    const DIRECTION_PERFEX_TO_MOLONI = 'perfex_to_moloni';
    const DIRECTION_MOLONI_TO_PERFEX = 'moloni_to_perfex';
    const DIRECTION_BIDIRECTIONAL = 'bidirectional';
    
    public function __construct()
    {
        $this->CI = &get_instance();
        $this->CI->load->model('desk_moloni_model');
        $this->model = $this->CI->desk_moloni_model;
        
        log_activity('EntityMappingService initialized');
    }
    
    /**
     * Create entity mapping
     * 
     * @param string $entity_type
     * @param int $perfex_id
     * @param int $moloni_id
     * @param string $sync_direction
     * @param array $metadata
     * @return int|false
     */
    public function create_mapping($entity_type, $perfex_id, $moloni_id, $sync_direction = self::DIRECTION_BIDIRECTIONAL, $metadata = [])
    {
        if (!$this->is_valid_entity_type($entity_type)) {
            throw new \InvalidArgumentException("Invalid entity type: {$entity_type}");
        }
        
        // Check for existing mapping
        $existing = $this->get_mapping($entity_type, $perfex_id, $moloni_id);
        if ($existing) {
            throw new \Exception("Mapping already exists with ID: {$existing->id}");
        }
        
        $mapping_data = [
            'entity_type' => $entity_type,
            'perfex_id' => $perfex_id,
            'moloni_id' => $moloni_id,
            'sync_direction' => $sync_direction,
            'sync_status' => self::STATUS_PENDING,
            'metadata' => json_encode($metadata),
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s')
        ];
        
        $mapping_id = $this->model->create_entity_mapping($mapping_data);
        
        if ($mapping_id) {
            log_activity("Created {$entity_type} mapping: Perfex #{$perfex_id} <-> Moloni #{$moloni_id}");
        }
        
        return $mapping_id;
    }
    
    /**
     * Update entity mapping
     * 
     * @param int $mapping_id
     * @param array $data
     * @return bool
     */
    public function update_mapping($mapping_id, $data)
    {
        $data['updated_at'] = date('Y-m-d H:i:s');
        
        $result = $this->model->update_entity_mapping($mapping_id, $data);
        
        if ($result) {
            log_activity("Updated entity mapping #{$mapping_id}");
        }
        
        return $result;
    }
    
    /**
     * Get entity mapping by IDs
     * 
     * @param string $entity_type
     * @param int $perfex_id
     * @param int $moloni_id
     * @return object|null
     */
    public function get_mapping($entity_type, $perfex_id = null, $moloni_id = null)
    {
        if (!$perfex_id && !$moloni_id) {
            throw new \InvalidArgumentException("Either perfex_id or moloni_id must be provided");
        }
        
        return $this->model->get_entity_mapping($entity_type, $perfex_id, $moloni_id);
    }
    
    /**
     * Get mapping by Perfex ID
     * 
     * @param string $entity_type
     * @param int $perfex_id
     * @return object|null
     */
    public function get_mapping_by_perfex_id($entity_type, $perfex_id)
    {
        return $this->model->get_entity_mapping_by_perfex_id($entity_type, $perfex_id);
    }
    
    /**
     * Get mapping by Moloni ID
     * 
     * @param string $entity_type
     * @param int $moloni_id
     * @return object|null
     */
    public function get_mapping_by_moloni_id($entity_type, $moloni_id)
    {
        return $this->model->get_entity_mapping_by_moloni_id($entity_type, $moloni_id);
    }
    
    /**
     * Delete entity mapping
     * 
     * @param int $mapping_id
     * @return bool
     */
    public function delete_mapping($mapping_id)
    {
        $mapping = $this->model->get_entity_mapping_by_id($mapping_id);
        
        if (!$mapping) {
            return false;
        }
        
        $result = $this->model->delete_entity_mapping($mapping_id);
        
        if ($result) {
            log_activity("Deleted {$mapping->entity_type} mapping #{$mapping_id}");
        }
        
        return $result;
    }
    
    /**
     * Get all mappings for entity type
     * 
     * @param string $entity_type
     * @param array $filters
     * @return array
     */
    public function get_mappings_by_type($entity_type, $filters = [])
    {
        if (!$this->is_valid_entity_type($entity_type)) {
            throw new \InvalidArgumentException("Invalid entity type: {$entity_type}");
        }
        
        return $this->model->get_entity_mappings_by_type($entity_type, $filters);
    }
    
    /**
     * Update mapping status
     * 
     * @param int $mapping_id
     * @param string $status
     * @param string $error_message
     * @return bool
     */
    public function update_mapping_status($mapping_id, $status, $error_message = null)
    {
        if (!in_array($status, [self::STATUS_PENDING, self::STATUS_SYNCED, self::STATUS_ERROR, self::STATUS_CONFLICT])) {
            throw new \InvalidArgumentException("Invalid status: {$status}");
        }
        
        $data = [
            'sync_status' => $status,
            'error_message' => $error_message,
            'last_sync_at' => date('Y-m-d H:i:s')
        ];
        
        return $this->update_mapping($mapping_id, $data);
    }
    
    /**
     * Update sync timestamps
     * 
     * @param int $mapping_id
     * @param string $direction
     * @return bool
     */
    public function update_sync_timestamp($mapping_id, $direction)
    {
        $field = $direction === self::DIRECTION_PERFEX_TO_MOLONI ? 'last_sync_perfex' : 'last_sync_moloni';
        
        return $this->update_mapping($mapping_id, [
            $field => date('Y-m-d H:i:s'),
            'sync_status' => self::STATUS_SYNCED
        ]);
    }
    
    /**
     * Check if entity is already mapped
     * 
     * @param string $entity_type
     * @param int $perfex_id
     * @param int $moloni_id
     * @return bool
     */
    public function is_mapped($entity_type, $perfex_id = null, $moloni_id = null)
    {
        return $this->get_mapping($entity_type, $perfex_id, $moloni_id) !== null;
    }
    
    /**
     * Get unmapped entities
     * 
     * @param string $entity_type
     * @param string $source_system ('perfex' or 'moloni')
     * @param int $limit
     * @return array
     */
    public function get_unmapped_entities($entity_type, $source_system, $limit = 100)
    {
        if (!$this->is_valid_entity_type($entity_type)) {
            throw new \InvalidArgumentException("Invalid entity type: {$entity_type}");
        }
        
        if (!in_array($source_system, ['perfex', 'moloni'])) {
            throw new \InvalidArgumentException("Invalid source system: {$source_system}");
        }
        
        return $this->model->get_unmapped_entities($entity_type, $source_system, $limit);
    }
    
    /**
     * Get mapping statistics
     * 
     * @param string $entity_type
     * @return array
     */
    public function get_mapping_statistics($entity_type = null)
    {
        return $this->model->get_mapping_statistics($entity_type);
    }
    
    /**
     * Find potential matches between systems
     * 
     * @param string $entity_type
     * @param array $search_criteria
     * @param string $target_system
     * @return array
     */
    public function find_potential_matches($entity_type, $search_criteria, $target_system)
    {
        if (!$this->is_valid_entity_type($entity_type)) {
            throw new \InvalidArgumentException("Invalid entity type: {$entity_type}");
        }
        
        // This will be implemented by specific sync services
        // Return format: [['id' => X, 'match_score' => Y, 'match_criteria' => []], ...]
        return [];
    }
    
    /**
     * Resolve mapping conflicts
     * 
     * @param int $mapping_id
     * @param string $resolution ('keep_perfex', 'keep_moloni', 'merge')
     * @param array $merge_data
     * @return bool
     */
    public function resolve_conflict($mapping_id, $resolution, $merge_data = [])
    {
        $mapping = $this->model->get_entity_mapping_by_id($mapping_id);
        
        if (!$mapping || $mapping->sync_status !== self::STATUS_CONFLICT) {
            throw new \Exception("Mapping not found or not in conflict state");
        }
        
        switch ($resolution) {
            case 'keep_perfex':
                return $this->update_mapping_status($mapping_id, self::STATUS_SYNCED);
                
            case 'keep_moloni':
                return $this->update_mapping_status($mapping_id, self::STATUS_SYNCED);
                
            case 'merge':
                // Store merge data for processing by sync services
                $metadata = json_decode($mapping->metadata, true) ?: [];
                $metadata['merge_data'] = $merge_data;
                $metadata['resolution'] = 'merge';
                
                return $this->update_mapping($mapping_id, [
                    'sync_status' => self::STATUS_PENDING,
                    'metadata' => json_encode($metadata)
                ]);
                
            default:
                throw new \InvalidArgumentException("Invalid resolution: {$resolution}");
        }
    }
    
    /**
     * Bulk create mappings
     * 
     * @param array $mappings
     * @return array
     */
    public function bulk_create_mappings($mappings)
    {
        $results = [
            'total' => count($mappings),
            'success' => 0,
            'errors' => 0,
            'details' => []
        ];
        
        foreach ($mappings as $mapping) {
            try {
                $mapping_id = $this->create_mapping(
                    $mapping['entity_type'],
                    $mapping['perfex_id'],
                    $mapping['moloni_id'],
                    $mapping['sync_direction'] ?? self::DIRECTION_BIDIRECTIONAL,
                    $mapping['metadata'] ?? []
                );
                
                $results['success']++;
                $results['details'][] = [
                    'mapping_id' => $mapping_id,
                    'success' => true
                ];
                
            } catch (\Exception $e) {
                $results['errors']++;
                $results['details'][] = [
                    'error' => $e->getMessage(),
                    'success' => false,
                    'data' => $mapping
                ];
            }
        }
        
        return $results;
    }
    
    /**
     * Clean up old mappings
     * 
     * @param string $entity_type
     * @param int $retention_days
     * @return int
     */
    public function cleanup_old_mappings($entity_type, $retention_days = 90)
    {
        $cutoff_date = date('Y-m-d H:i:s', strtotime("-{$retention_days} days"));
        
        $deleted = $this->model->cleanup_old_mappings($entity_type, $cutoff_date);
        
        if ($deleted > 0) {
            log_activity("Cleaned up {$deleted} old {$entity_type} mappings older than {$retention_days} days");
        }
        
        return $deleted;
    }
    
    /**
     * Validate entity type
     * 
     * @param string $entity_type
     * @return bool
     */
    protected function is_valid_entity_type($entity_type)
    {
        return in_array($entity_type, [
            self::ENTITY_CUSTOMER,
            self::ENTITY_PRODUCT,
            self::ENTITY_INVOICE,
            self::ENTITY_ESTIMATE,
            self::ENTITY_CREDIT_NOTE
        ]);
    }
    
    /**
     * Export mappings to CSV
     * 
     * @param string $entity_type
     * @param array $filters
     * @return string
     */
    public function export_mappings_csv($entity_type, $filters = [])
    {
        $mappings = $this->get_mappings_by_type($entity_type, $filters);
        
        $output = fopen('php://temp', 'r+');
        
        // CSV Header
        fputcsv($output, [
            'ID',
            'Entity Type',
            'Perfex ID',
            'Moloni ID',
            'Sync Direction',
            'Sync Status',
            'Last Sync Perfex',
            'Last Sync Moloni',
            'Created At',
            'Updated At'
        ]);
        
        foreach ($mappings as $mapping) {
            fputcsv($output, [
                $mapping->id,
                $mapping->entity_type,
                $mapping->perfex_id,
                $mapping->moloni_id,
                $mapping->sync_direction,
                $mapping->sync_status,
                $mapping->last_sync_perfex,
                $mapping->last_sync_moloni,
                $mapping->created_at,
                $mapping->updated_at
            ]);
        }
        
        rewind($output);
        $csv_content = stream_get_contents($output);
        fclose($output);
        
        return $csv_content;
    }
}