<?php

declare(strict_types=1);

namespace DeskMoloni\Tests;

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

use PHPUnit\Framework\TestCase as PHPUnitTestCase;
use stdClass;
use Exception;

/**
 * TestCase.php
 * 
 * Base test case for Desk-Moloni v3.0 integration tests
 * Provides common setup and utilities for database testing
 * 
 * @package DeskMoloni\Tests
 * @author Database Design Specialist  
 * @version 3.0
 */
abstract class TestCase extends PHPUnitTestCase
{
    protected $ci;
    protected $db;
    
    public function setUp(): void
    {
        parent::setUp();
        
        // Initialize CodeIgniter instance for testing
        $this->initializeCodeIgniter();
        
        // Set up database connection
        $this->setupDatabase();
    }
    
    public function tearDown(): void
    {
        // Clean up any resources
        parent::tearDown();
    }
    
    /**
     * Initialize CodeIgniter for testing
     */
    protected function initializeCodeIgniter()
    {
        // Mock CodeIgniter instance for testing
        $this->ci = new stdClass();
        
        // Mock database object
        $this->ci->db = $this->createDatabaseMock();
        
        // Set global CI instance
        if (!function_exists('get_instance')) {
            function get_instance() {
                return $GLOBALS['CI_INSTANCE'];
            }
        }
        $GLOBALS['CI_INSTANCE'] = $this->ci;
    }
    
    /**
     * Create database mock for testing
     */
    protected function createDatabaseMock()
    {
        return new DatabaseMock();
    }
    
    /**
     * Set up database connection and tables
     */
    protected function setupDatabase()
    {
        $this->db = $this->ci->db;
        
        // Ensure test tables exist
        $this->createTestTables();
    }
    
    /**
     * Create test tables if they don't exist
     */
    protected function createTestTables()
    {
        // This would normally create actual test tables
        // For now, we'll mock this functionality
    }
    
    /**
     * Execute a raw SQL query for testing
     */
    protected function executeRawSQL($sql)
    {
        return $this->db->query($sql);
    }
    
    /**
     * Get table structure information
     */
    protected function getTableStructure($tableName)
    {
        return $this->db->field_data($tableName);
    }
    
    /**
     * Clean up test data
     */
    protected function cleanupTestData($tableName, $conditions = [])
    {
        $this->db->delete($tableName, $conditions);
    }
}

/**
 * Mock Database class for testing when real database is not available
 */
class DatabaseMock
{
    private $lastQuery = '';
    private $lastError = ['code' => 0, 'message' => ''];
    private $insertSuccess = true;
    private $mockData = [];
    
    public function table_exists($tableName)
    {
        // Mock table existence based on expected Desk-Moloni tables
        $expectedTables = [
            'desk_moloni_config',
            'desk_moloni_mapping', 
            'desk_moloni_sync_queue',
            'desk_moloni_sync_log'
        ];
        
        return in_array($tableName, $expectedTables);
    }
    
    public function field_exists($fieldName, $tableName)
    {
        // Mock field existence based on table structure
        $tableFields = [
            'desk_moloni_config' => [
                'id', 'setting_key', 'setting_value', 'encrypted', 'created_at', 'updated_at'
            ],
            'desk_moloni_mapping' => [
                'id', 'entity_type', 'perfex_id', 'moloni_id', 'sync_direction', 
                'last_sync_at', 'created_at', 'updated_at'
            ],
            'desk_moloni_sync_queue' => [
                'id', 'task_type', 'entity_type', 'entity_id', 'priority', 'payload',
                'status', 'attempts', 'max_attempts', 'scheduled_at', 'started_at', 
                'completed_at', 'error_message', 'created_at', 'updated_at'
            ],
            'desk_moloni_sync_log' => [
                'id', 'operation_type', 'entity_type', 'perfex_id', 'moloni_id',
                'direction', 'status', 'request_data', 'response_data', 'error_message',
                'execution_time_ms', 'created_at'
            ]
        ];
        
        return isset($tableFields[$tableName]) && in_array($fieldName, $tableFields[$tableName]);
    }
    
    public function field_data($tableName)
    {
        // Mock field data structure
        $mockField = new stdClass();
        $mockField->name = 'id';
        $mockField->type = 'int';
        $mockField->primary_key = 1;
        
        return [$mockField];
    }
    
    public function insert($tableName, $data)
    {
        $this->lastQuery = "INSERT INTO {$tableName}";
        
        // Mock insert validation
        if (isset($data['setting_key']) && $data['setting_key'] === 'test_unique_key') {
            static $inserted = false;
            if ($inserted) {
                $this->lastError = ['code' => 1062, 'message' => 'Duplicate entry'];
                return false;
            }
            $inserted = true;
        }
        
        // Store mock data for retrieval
        $data['id'] = rand(1, 1000);
        $data['created_at'] = date('Y-m-d H:i:s');
        $data['updated_at'] = date('Y-m-d H:i:s');
        $this->mockData[] = (object) $data;
        
        return $this->insertSuccess;
    }
    
    public function update($tableName, $data, $where = null)
    {
        $this->lastQuery = "UPDATE {$tableName}";
        return true;
    }
    
    public function delete($tableName, $where = null)
    {
        $this->lastQuery = "DELETE FROM {$tableName}";
        return true;
    }
    
    public function where($field, $value = null)
    {
        return $this;
    }
    
    public function order_by($field, $direction = 'ASC')
    {
        return $this;
    }
    
    public function limit($limit, $offset = null)
    {
        return $this;
    }
    
    public function get($tableName = null)
    {
        $result = new stdClass();
        $result->row_array = [];
        $result->result_array = $this->mockData;
        $result->row = function() {
            return !empty($this->mockData) ? $this->mockData[0] : null;
        };
        $result->result = function() {
            return $this->mockData;
        };
        
        return $result;
    }
    
    public function count_all_results($tableName)
    {
        return count($this->mockData);
    }
    
    public function query($sql)
    {
        $this->lastQuery = $sql;
        
        // Mock specific queries
        if (strpos($sql, 'SHOW INDEX') !== false) {
            $mockIndexes = [
                ['Key_name' => 'PRIMARY'],
                ['Key_name' => 'idx_setting_key'],
                ['Key_name' => 'idx_encrypted'],
                ['Key_name' => 'idx_created_at']
            ];
            
            $result = new stdClass();
            $result->result_array = function() use ($mockIndexes) { return $mockIndexes; };
            return $result;
        }
        
        if (strpos($sql, 'TABLE_COLLATION') !== false) {
            $result = new stdClass();
            $result->row = function() {
                $row = new stdClass();
                $row->TABLE_COLLATION = 'utf8mb4_unicode_ci';
                return $row;
            };
            return $result;
        }
        
        if (strpos($sql, 'ENGINE') !== false) {
            $result = new stdClass();
            $result->row = function() {
                $row = new stdClass();
                $row->ENGINE = 'InnoDB';
                return $row;
            };
            return $result;
        }
        
        return $this->get();
    }
    
    public function error()
    {
        return $this->lastError;
    }
    
    public function insert_id()
    {
        return rand(1, 1000);
    }
    
    // Reset mock state
    public function reset()
    {
        $this->mockData = [];
        $this->lastError = ['code' => 0, 'message' => ''];
        $this->insertSuccess = true;
    }
}