<?php
/**
 * DualPDO - Replicación de Base de Datos Transparente en Tiempo Real (PRODUCCIÓN / WEB HOSTING)
 * 
 * Configuración para Web Hosting:
 * - Principal: farmacia_nadiecomoel2026
 * - Secundaria: farmacia_nadiecomoel
 */

class DualPDO extends PDO
{
    private $pdo2;

    public function __construct($dsn1, $username1, $password1, $options1, $pdo2)
    {
        parent::__construct($dsn1, $username1, $password1, $options1);
        $this->pdo2 = $pdo2;
    }

    #[\ReturnTypeWillChange]
    public function prepare($query, $options = [])
    {
        $stmt1 = parent::prepare($query, $options);
        $stmt2 = null;
        if ($this->pdo2) {
            try {
                $stmt2 = $this->pdo2->prepare($query, $options);
            } catch (Exception $e) {
                error_log("DualPDO: Error al preparar sentencia en BD secundaria: " . $e->getMessage());
            }
        }
        return new DualPDOStatement($stmt1, $stmt2);
    }

    #[\ReturnTypeWillChange]
    public function query($query, ...$args)
    {
        $res1 = parent::query($query, ...$args);
        if ($this->pdo2) {
            try {
                $this->pdo2->query($query, ...$args);
            } catch (Exception $e) {
                error_log("DualPDO: Error al ejecutar query en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    #[\ReturnTypeWillChange]
    public function exec($statement)
    {
        $res1 = parent::exec($statement);
        if ($this->pdo2) {
            try {
                $this->pdo2->exec($statement);
            } catch (Exception $e) {
                error_log("DualPDO: Error al ejecutar exec en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    #[\ReturnTypeWillChange]
    public function beginTransaction()
    {
        $res1 = parent::beginTransaction();
        if ($this->pdo2) {
            try {
                $this->pdo2->beginTransaction();
            } catch (Exception $e) {
                error_log("DualPDO: Error al iniciar transacción en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    #[\ReturnTypeWillChange]
    public function commit()
    {
        $res1 = parent::commit();
        if ($this->pdo2) {
            try {
                $this->pdo2->commit();
            } catch (Exception $e) {
                error_log("DualPDO: Error al confirmar transacción en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    #[\ReturnTypeWillChange]
    public function rollBack()
    {
        $res1 = parent::rollBack();
        if ($this->pdo2) {
            try {
                $this->pdo2->rollBack();
            } catch (Exception $e) {
                error_log("DualPDO: Error al revertir transacción en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }
}

class DualPDOStatement implements IteratorAggregate
{
    private $stmt1;
    private $stmt2;

    public function __construct($stmt1, $stmt2)
    {
        $this->stmt1 = $stmt1;
        $this->stmt2 = $stmt2;
    }

    public function execute($params = null)
    {
        $res1 = $this->stmt1->execute($params);
        if ($this->stmt2) {
            try {
                $this->stmt2->execute($params);
            } catch (Exception $e) {
                error_log("DualPDOStatement: Error al ejecutar en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR)
    {
        $res1 = $this->stmt1->bindValue($parameter, $value, $data_type);
        if ($this->stmt2) {
            try {
                $this->stmt2->bindValue($parameter, $value, $data_type);
            } catch (Exception $e) {
                error_log("DualPDOStatement: Error al asociar valor en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length = null, $driver_options = null)
    {
        $res1 = $this->stmt1->bindParam($parameter, $variable, $data_type, $length, $driver_options);
        if ($this->stmt2) {
            try {
                $this->stmt2->bindParam($parameter, $variable, $data_type, $length, $driver_options);
            } catch (Exception $e) {
                error_log("DualPDOStatement: Error al asociar parámetro en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    public function fetch($mode = null, $cursor_orientation = null, $cursor_offset = null)
    {
        if ($mode === null) {
            return $this->stmt1->fetch();
        }
        if ($cursor_orientation === null && $cursor_offset === null) {
            return $this->stmt1->fetch($mode);
        }
        if ($cursor_offset === null) {
            return $this->stmt1->fetch($mode, $cursor_orientation);
        }
        return $this->stmt1->fetch($mode, $cursor_orientation, $cursor_offset);
    }

    public function fetchAll($mode = null, ...$args)
    {
        if ($mode === null) {
            return $this->stmt1->fetchAll();
        }
        return $this->stmt1->fetchAll($mode, ...$args);
    }

    public function fetchColumn($column_number = 0)
    {
        return $this->stmt1->fetchColumn($column_number);
    }

    public function rowCount()
    {
        return $this->stmt1->rowCount();
    }

    #[\ReturnTypeWillChange]
    public function getIterator()
    {
        return $this->stmt1;
    }

    public function __call($name, $arguments)
    {
        $res = call_user_func_array([$this->stmt1, $name], $arguments);
        if ($this->stmt2 && is_callable([$this->stmt2, $name])) {
            try {
                call_user_func_array([$this->stmt2, $name], $arguments);
            } catch (Exception $e) {
                error_log("DualPDOStatement: Error al delegar método '$name' en BD secundaria: " . $e->getMessage());
            }
        }
        return $res;
    }

    public function __get($name)
    {
        return $this->stmt1->$name;
    }
}

/**
 * DualMysqli - Replicación de Base de Datos Transparente para MySQLi
 */
class DualMysqli extends mysqli
{
    private $conn2;

    public function __construct($host, $user, $password, $db, $port = null, $socket = null, $conn2 = null)
    {
        parent::__construct($host, $user, $password, $db, $port, $socket);
        $this->conn2 = $conn2;
    }

    #[\ReturnTypeWillChange]
    public function prepare($query)
    {
        $stmt1 = parent::prepare($query);
        $stmt2 = null;
        if ($this->conn2) {
            try {
                $stmt2 = $this->conn2->prepare($query);
            } catch (Exception $e) {
                error_log("DualMysqli: Error al preparar sentencia en BD secundaria: " . $e->getMessage());
            }
        }
        return new DualMysqliStmt($stmt1, $stmt2);
    }

    #[\ReturnTypeWillChange]
    public function query($query, $result_mode = MYSQLI_STORE_RESULT)
    {
        $res1 = parent::query($query, $result_mode);
        if ($this->conn2) {
            try {
                $this->conn2->query($query, $result_mode);
            } catch (Exception $e) {
                error_log("DualMysqli: Error al ejecutar query en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    #[\ReturnTypeWillChange]
    public function begin_transaction($flags = 0, $name = null)
    {
        $res1 = parent::begin_transaction($flags, $name);
        if ($this->conn2) {
            try {
                $this->conn2->begin_transaction($flags, $name);
            } catch (Exception $e) {}
        }
        return $res1;
    }

    #[\ReturnTypeWillChange]
    public function commit($flags = 0, $name = null)
    {
        $res1 = parent::commit($flags, $name);
        if ($this->conn2) {
            try {
                $this->conn2->commit($flags, $name);
            } catch (Exception $e) {}
        }
        return $res1;
    }

    #[\ReturnTypeWillChange]
    public function rollback($flags = 0, $name = null)
    {
        $res1 = parent::rollback($flags, $name);
        if ($this->conn2) {
            try {
                $this->conn2->rollback($flags, $name);
            } catch (Exception $e) {}
        }
        return $res1;
    }
}

class DualMysqliStmt
{
    private $stmt1;
    private $stmt2;

    public function __construct($stmt1, $stmt2)
    {
        $this->stmt1 = $stmt1;
        $this->stmt2 = $stmt2;
    }

    public function bind_param($types, &...$vars)
    {
        $res1 = $this->stmt1->bind_param($types, ...$vars);
        if ($this->stmt2) {
            try {
                $this->stmt2->bind_param($types, ...$vars);
            } catch (Exception $e) {
                error_log("DualMysqliStmt: Error al asociar parámetros en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    public function execute()
    {
        $res1 = $this->stmt1->execute();
        if ($this->stmt2) {
            try {
                $this->stmt2->execute();
            } catch (Exception $e) {
                error_log("DualMysqliStmt: Error al ejecutar en BD secundaria: " . $e->getMessage());
            }
        }
        return $res1;
    }

    public function get_result()
    {
        return $this->stmt1->get_result();
    }

    public function close()
    {
        $res1 = $this->stmt1->close();
        if ($this->stmt2) {
            try {
                $this->stmt2->close();
            } catch (Exception $e) {}
        }
        return $res1;
    }

    public function __call($name, $arguments)
    {
        $res = call_user_func_array([$this->stmt1, $name], $arguments);
        if ($this->stmt2 && is_callable([$this->stmt2, $name])) {
            try {
                call_user_func_array([$this->stmt2, $name], $arguments);
            } catch (Exception $e) {
                error_log("DualMysqliStmt: Error al delegar método '$name' en BD secundaria: " . $e->getMessage());
            }
        }
        return $res;
    }

    public function __get($name)
    {
        return $this->stmt1->$name;
    }
}

/**
 * Crea o recupera una conexión dual (PDO o DualPDO) basada en la configuración para Hosting.
 */
function getDualConnection($config)
{
    try {
        $dsn1 = "mysql:host=" . $config['host'] . ";port=" . $config['port'] . ";dbname=" . $config['dbname'] . ";charset=" . $config['charset'];
        
        $secondaryDb = isset($config['secondary_dbname']) ? $config['secondary_dbname'] : (($config['dbname'] === 'farmacia_nadiecomoel2026') ? 'farmacia_nadiecomoel' : 'farmacia_nadiecomoel2026');
        
        $dsn2 = "mysql:host=" . $config['host'] . ";port=" . $config['port'] . ";dbname=" . $secondaryDb . ";charset=" . $config['charset'];
        
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ];
        
        if (isset($config['options']) && is_array($config['options'])) {
            $options = $config['options'] + $options;
        }

        $pdo2 = null;
        try {
            $pdo2 = new PDO($dsn2, $config['user'], $config['pass'], $options);
        } catch (PDOException $e2) {
            error_log("DualPDO: No se pudo conectar a la base de datos secundaria '$secondaryDb': " . $e2->getMessage());
        }
        
        if ($pdo2 !== null) {
            return new DualPDO($dsn1, $config['user'], $config['pass'], $options, $pdo2);
        } else {
            return new PDO($dsn1, $config['user'], $config['pass'], $options);
        }
    } catch (PDOException $e) {
        die("Error de conexión: " . $e->getMessage());
    }
}

/**
 * Crea o recupera una conexión dual MySQLi basada en la configuración para Hosting.
 */
function getDualMysqliConnection($config)
{
    try {
        $host = $config['host'];
        $db = $config['dbname'];
        $user = $config['user'];
        $password = $config['pass'];
        $port = isset($config['port']) ? $config['port'] : null;
        
        $secondaryDb = isset($config['secondary_dbname']) ? $config['secondary_dbname'] : (($db === 'farmacia_nadiecomoel2026') ? 'farmacia_nadiecomoel' : 'farmacia_nadiecomoel2026');
        
        $conn2 = null;
        try {
            $conn2 = @new mysqli($host, $user, $password, $secondaryDb, $port);
            if ($conn2->connect_error) {
                $conn2 = null;
            }
        } catch (Exception $e2) {
            $conn2 = null;
        }

        if ($conn2 !== null) {
            $conn1 = new DualMysqli($host, $user, $password, $db, $port, null, $conn2);
            if ($conn1->connect_error) {
                die("Conexión fallida: " . $conn1->connect_error);
            }
            $conn1->set_charset("utf8mb4");
            return $conn1;
        } else {
            $conn1 = new mysqli($host, $user, $password, $db, $port);
            if ($conn1->connect_error) {
                die("Conexión fallida: " . $conn1->connect_error);
            }
            $conn1->set_charset("utf8mb4");
            return $conn1;
        }
    } catch (Exception $e) {
        die("Error de conexión: " . $e->getMessage());
    }
}
