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

<?php

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

/**
 * Client Sync Integration Tests
 * End-to-end integration tests for client synchronization between Perfex CRM and Moloni ERP
 * 
 * @package DeskMoloni
 * @subpackage Tests\Integration
 * @category IntegrationTests
 * @author Descomplicar® - PHP Fullstack Engineer
 * @version 1.0.0
 */

use PHPUnit\Framework\TestCase;
use DeskMoloni\Libraries\ClientSyncService;
use DeskMoloni\Libraries\EntityMappingService;
use DeskMoloni\Libraries\MoloniApiClient;

class ClientSyncIntegrationTest extends TestCase
{
    protected $client_sync;
    protected $entity_mapping;
    protected $api_client_mock;
    protected $test_client_data;
    protected $test_moloni_data;
    
    protected function setUp(): void
    {
        parent::setUp();
        
        // Initialize services
        $this->client_sync = new ClientSyncService();
        $this->entity_mapping = new EntityMappingService();
        
        // Mock API client
        $this->api_client_mock = $this->createMock(MoloniApiClient::class);
        
        // Set up test data
        $this->setupTestData();
        
        // Inject mocked API client
        $reflection = new ReflectionClass($this->client_sync);
        $property = $reflection->getProperty('api_client');
        $property->setAccessible(true);
        $property->setValue($this->client_sync, $this->api_client_mock);
    }
    
    protected function setupTestData()
    {
        $this->test_client_data = [
            'userid' => 999,
            'company' => 'Test Company Ltd',
            'vat' => 'PT123456789',
            'email' => 'test@testcompany.com',
            'phonenumber' => '+351234567890',
            'website' => 'https://testcompany.com',
            'billing_street' => 'Test Street, 123',
            'billing_city' => 'Lisbon',
            'billing_state' => 'Lisboa',
            'billing_zip' => '1000-001',
            'billing_country' => 'PT',
            'admin_notes' => 'Test client for integration testing'
        ];
        
        $this->test_moloni_data = [
            'customer_id' => 888,
            'name' => 'Test Company Ltd',
            'vat' => 'PT123456789',
            'email' => 'test@testcompany.com',
            'phone' => '+351234567890',
            'website' => 'https://testcompany.com',
            'address' => 'Test Street, 123',
            'city' => 'Lisbon',
            'state' => 'Lisboa',
            'zip_code' => '1000-001',
            'country_id' => 1,
            'notes' => 'Test client for integration testing'
        ];
    }
    
    /**
     * Test complete Perfex to Moloni sync workflow
     */
    public function test_complete_perfex_to_moloni_sync_workflow()
    {
        // Arrange
        $perfex_client_id = $this->test_client_data['userid'];
        
        // Mock Perfex client retrieval
        $this->mockPerfexClientRetrieval($perfex_client_id, $this->test_client_data);
        
        // Mock successful Moloni API creation
        $this->api_client_mock->expects($this->once())
            ->method('create_customer')
            ->willReturn([
                'success' => true,
                'data' => [
                    'customer_id' => $this->test_moloni_data['customer_id']
                ]
            ]);
        
        // Act
        $result = $this->client_sync->sync_perfex_to_moloni($perfex_client_id);
        
        // Assert
        $this->assertTrue($result['success']);
        $this->assertEquals('create', $result['action']);
        $this->assertEquals($this->test_moloni_data['customer_id'], $result['moloni_customer_id']);
        $this->assertIsInt($result['mapping_id']);
        $this->assertGreaterThan(0, $result['execution_time']);
        
        // Verify mapping was created
        $mapping = $this->entity_mapping->get_mapping_by_perfex_id(
            EntityMappingService::ENTITY_CUSTOMER,
            $perfex_client_id
        );
        
        $this->assertNotNull($mapping);
        $this->assertEquals($perfex_client_id, $mapping->perfex_id);
        $this->assertEquals($this->test_moloni_data['customer_id'], $mapping->moloni_id);
        $this->assertEquals(EntityMappingService::STATUS_SYNCED, $mapping->sync_status);
    }
    
    /**
     * Test complete Moloni to Perfex sync workflow
     */
    public function test_complete_moloni_to_perfex_sync_workflow()
    {
        // Arrange
        $moloni_customer_id = $this->test_moloni_data['customer_id'];
        
        // Mock Moloni API response
        $this->api_client_mock->expects($this->once())
            ->method('get_customer')
            ->with($moloni_customer_id)
            ->willReturn([
                'success' => true,
                'data' => $this->test_moloni_data
            ]);
        
        // Mock Perfex client creation
        $this->mockPerfexClientCreation($this->test_client_data['userid']);
        
        // Act
        $result = $this->client_sync->sync_moloni_to_perfex($moloni_customer_id);
        
        // Assert
        $this->assertTrue($result['success']);
        $this->assertEquals('create', $result['action']);
        $this->assertEquals($this->test_client_data['userid'], $result['perfex_client_id']);
        $this->assertIsInt($result['mapping_id']);
        
        // Verify mapping was created
        $mapping = $this->entity_mapping->get_mapping_by_moloni_id(
            EntityMappingService::ENTITY_CUSTOMER,
            $moloni_customer_id
        );
        
        $this->assertNotNull($mapping);
        $this->assertEquals($this->test_client_data['userid'], $mapping->perfex_id);
        $this->assertEquals($moloni_customer_id, $mapping->moloni_id);
        $this->assertEquals(EntityMappingService::STATUS_SYNCED, $mapping->sync_status);
    }
    
    /**
     * Test sync with existing mapping (update scenario)
     */
    public function test_sync_with_existing_mapping_update()
    {
        // Arrange - Create existing mapping
        $perfex_client_id = $this->test_client_data['userid'];
        $moloni_customer_id = $this->test_moloni_data['customer_id'];
        
        $mapping_id = $this->entity_mapping->create_mapping(
            EntityMappingService::ENTITY_CUSTOMER,
            $perfex_client_id,
            $moloni_customer_id,
            EntityMappingService::DIRECTION_PERFEX_TO_MOLONI
        );
        
        // Mock Perfex client retrieval
        $this->mockPerfexClientRetrieval($perfex_client_id, $this->test_client_data);
        
        // Mock successful Moloni API update
        $this->api_client_mock->expects($this->once())
            ->method('update_customer')
            ->with($moloni_customer_id)
            ->willReturn([
                'success' => true,
                'data' => $this->test_moloni_data
            ]);
        
        // Act
        $result = $this->client_sync->sync_perfex_to_moloni($perfex_client_id, true); // Force update
        
        // Assert
        $this->assertTrue($result['success']);
        $this->assertEquals('update', $result['action']);
        $this->assertEquals($mapping_id, $result['mapping_id']);
    }
    
    /**
     * Test conflict detection and handling
     */
    public function test_conflict_detection_and_handling()
    {
        // Arrange - Create mapping with conflicting data
        $perfex_client_id = $this->test_client_data['userid'];
        $moloni_customer_id = $this->test_moloni_data['customer_id'];
        
        $mapping_id = $this->entity_mapping->create_mapping(
            EntityMappingService::ENTITY_CUSTOMER,
            $perfex_client_id,
            $moloni_customer_id,
            EntityMappingService::DIRECTION_BIDIRECTIONAL
        );
        
        // Set last sync time in the past
        $this->entity_mapping->update_mapping($mapping_id, [
            'last_sync_perfex' => date('Y-m-d H:i:s', strtotime('-1 hour')),
            'last_sync_moloni' => date('Y-m-d H:i:s', strtotime('-1 hour'))
        ]);
        
        // Mock Perfex client with recent changes
        $modified_client_data = $this->test_client_data;
        $modified_client_data['company'] = 'Modified Company Name';
        $this->mockPerfexClientRetrieval($perfex_client_id, $modified_client_data);
        
        // Mock Moloni customer with different recent changes
        $modified_moloni_data = $this->test_moloni_data;
        $modified_moloni_data['name'] = 'Different Modified Name';
        
        $this->api_client_mock->expects($this->once())
            ->method('get_customer')
            ->with($moloni_customer_id)
            ->willReturn([
                'success' => true,
                'data' => $modified_moloni_data
            ]);
        
        // Mock modification time methods
        $this->mockModificationTimes();
        
        // Act
        $result = $this->client_sync->sync_perfex_to_moloni($perfex_client_id);
        
        // Assert conflict detected
        $this->assertFalse($result['success']);
        $this->assertArrayHasKey('conflict_details', $result);
        $this->assertTrue($result['requires_manual_resolution']);
        
        // Verify mapping status updated to conflict
        $updated_mapping = $this->entity_mapping->get_mapping(
            EntityMappingService::ENTITY_CUSTOMER,
            $perfex_client_id,
            $moloni_customer_id
        );
        
        $this->assertEquals(EntityMappingService::STATUS_CONFLICT, $updated_mapping->sync_status);
    }
    
    /**
     * Test error handling for API failures
     */
    public function test_error_handling_for_api_failures()
    {
        // Arrange
        $perfex_client_id = $this->test_client_data['userid'];
        
        $this->mockPerfexClientRetrieval($perfex_client_id, $this->test_client_data);
        
        // Mock API failure
        $this->api_client_mock->expects($this->once())
            ->method('create_customer')
            ->willReturn([
                'success' => false,
                'message' => 'API authentication failed'
            ]);
        
        // Act
        $result = $this->client_sync->sync_perfex_to_moloni($perfex_client_id);
        
        // Assert
        $this->assertFalse($result['success']);
        $this->assertStringContains('Moloni API error', $result['message']);
        $this->assertGreaterThan(0, $result['execution_time']);
    }
    
    /**
     * Test customer matching functionality
     */
    public function test_customer_matching_functionality()
    {
        // Arrange
        $search_data = [
            'company' => 'Test Company Ltd',
            'vat' => 'PT123456789',
            'email' => 'test@testcompany.com'
        ];
        
        // Mock API search responses
        $this->api_client_mock->expects($this->once())
            ->method('search_customers')
            ->with(['vat' => $search_data['vat']])
            ->willReturn([
                'success' => true,
                'data' => [
                    [
                        'customer_id' => 888,
                        'name' => $search_data['company'],
                        'vat' => $search_data['vat']
                    ]
                ]
            ]);
        
        // Act
        $matches = $this->client_sync->find_moloni_customer_matches($search_data);
        
        // Assert
        $this->assertIsArray($matches);
        $this->assertNotEmpty($matches);
        $this->assertEquals(ClientSyncService::MATCH_SCORE_EXACT, $matches[0]['match_score']);
        $this->assertEquals('vat', $matches[0]['match_type']);
    }
    
    /**
     * Test batch sync functionality
     */
    public function test_batch_sync_functionality()
    {
        // Arrange
        $client_ids = [100, 101, 102];
        
        foreach ($client_ids as $client_id) {
            $client_data = $this->test_client_data;
            $client_data['userid'] = $client_id;
            $this->mockPerfexClientRetrieval($client_id, $client_data);
        }
        
        // Mock successful API responses
        $this->api_client_mock->expects($this->exactly(3))
            ->method('create_customer')
            ->willReturn([
                'success' => true,
                'data' => ['customer_id' => 999]
            ]);
        
        // Act
        $result = $this->client_sync->batch_sync_customers($client_ids);
        
        // Assert
        $this->assertIsArray($result);
        $this->assertEquals(3, $result['total']);
        $this->assertEquals(3, $result['success']);
        $this->assertEquals(0, $result['errors']);
        $this->assertCount(3, $result['details']);
    }
    
    
    
    /**
     * Test sync statistics tracking
     */
    public function test_sync_statistics_tracking()
    {
        // Arrange - Perform several sync operations
        $this->setupMultipleSyncOperations();
        
        // Act
        $stats = $this->client_sync->get_sync_statistics();
        
        // Assert
        $this->assertIsArray($stats);
        $this->assertArrayHasKey('total_customers', $stats);
        $this->assertArrayHasKey('synced_customers', $stats);
        $this->assertArrayHasKey('pending_customers', $stats);
        $this->assertArrayHasKey('error_customers', $stats);
        $this->assertArrayHasKey('last_sync', $stats);
    }
    
    // Helper methods
    
    protected function mockPerfexClientRetrieval($client_id, $client_data)
    {
        // Mock CodeIgniter instance and clients_model
        $CI = $this->createMock(stdClass::class);
        $clients_model = $this->createMock(stdClass::class);
        
        $clients_model->expects($this->any())
            ->method('get')
            ->with($client_id)
            ->willReturn((object)$client_data);
        
        // This would need proper CI mock injection in real implementation
    }
    
    protected function mockPerfexClientCreation($expected_client_id)
    {
        // Mock successful client creation in Perfex
        // This would need proper CI mock injection in real implementation
    }
    
    protected function mockModificationTimes()
    {
        // Mock modification time retrieval methods
        $reflection = new ReflectionClass($this->client_sync);
        
        $perfex_time_method = $reflection->getMethod('get_perfex_modification_time');
        $perfex_time_method->setAccessible(true);
        
        $moloni_time_method = $reflection->getMethod('get_moloni_modification_time');
        $moloni_time_method->setAccessible(true);
        
        // Set times to simulate recent modifications on both sides
        // Implementation would need proper mocking
    }
    
    protected function setupMultipleSyncOperations()
    {
        // Setup multiple test sync operations for statistics testing
        // This would involve creating multiple mappings and sync logs
    }
    
    protected function tearDown(): void
    {
        // Clean up test data
        $this->cleanupTestData();
        parent::tearDown();
    }
    
    protected function cleanupTestData()
    {
        // Remove test mappings and sync logs
        // This would need proper database cleanup
    }
}