<?php

class Database {
    private static $instance = null;
    private $pdo;
    private $host;
    private $dbname;
    private $username;
    private $password;
    private $charset = 'utf8mb4';
    private $options = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,
        PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
    ];
    
    private function __construct() {
        $this->loadEnvironment();
        $this->connect();
    }
    
    private function loadEnvironment() {
        $envFile = dirname(__DIR__) . '/.env';
        if (file_exists($envFile)) {
            $lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            foreach ($lines as $line) {
                if (strpos(trim($line), '#') === 0) continue;
                
                list($name, $value) = explode('=', $line, 2);
                $name = trim($name);
                $value = trim($value);
                
                if (!isset($_ENV[$name])) {
                    putenv(sprintf('%s=%s', $name, $value));
                    $_ENV[$name] = $value;
                    $_SERVER[$name] = $value;
                }
            }
        }
        
        $this->host = $_ENV['DB_HOST'] ?? 'localhost';
        $this->dbname = $_ENV['DB_DATABASE'] ?? 'cleo';
        $this->username = $_ENV['DB_USERNAME'] ?? 'root';
        $this->password = $_ENV['DB_PASSWORD'] ?? '';
    }
    
    private function connect() {
        try {
            $dsn = "mysql:host={$this->host};dbname={$this->dbname};charset={$this->charset}";
            $this->pdo = new PDO($dsn, $this->username, $this->password, $this->options);
            
            if ($_ENV['LOG_SQL'] ?? false) {
                $this->logConnection(true);
            }
        } catch (PDOException $e) {
            if ($_ENV['APP_DEBUG'] ?? false) {
                throw new Exception("Erreur de connexion à la base de données: " . $e->getMessage());
            } else {
                throw new Exception("Erreur de connexion à la base de données");
            }
        }
    }
    
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public function getPDO() {
        return $this->pdo;
    }
    
    public function query($sql, $params = []) {
        $start = microtime(true);
        
        try {
            if (empty($params)) {
                $stmt = $this->pdo->query($sql);
            } else {
                $stmt = $this->pdo->prepare($sql);
                $stmt->execute($params);
            }
            
            $this->logQuery($sql, $params, microtime(true) - $start);
            return $stmt;
            
        } catch (PDOException $e) {
            $this->logError($sql, $params, $e->getMessage());
            throw $e;
        }
    }
    
    public function fetchAll($sql, $params = []) {
        $stmt = $this->query($sql, $params);
        return $stmt->fetchAll();
    }
    
    public function fetchOne($sql, $params = []) {
        $stmt = $this->query($sql, $params);
        return $stmt->fetch();
    }
    
    public function fetchColumn($sql, $params = [], $column = 0) {
        $stmt = $this->query($sql, $params);
        return $stmt->fetchColumn($column);
    }
    
    public function insert($table, $data) {
        $columns = array_keys($data);
        $values = array_map(function($col) { return ':' . $col; }, $columns);
        
        $sql = sprintf(
            "INSERT INTO %s (%s) VALUES (%s)",
            $table,
            implode(', ', $columns),
            implode(', ', $values)
        );
        
        $this->query($sql, $data);
        return $this->pdo->lastInsertId();
    }
    
    public function update($table, $data, $where, $whereParams = []) {
        $set = [];
        foreach ($data as $column => $value) {
            $set[] = "$column = :set_$column";
        }
        
        $sql = sprintf(
            "UPDATE %s SET %s WHERE %s",
            $table,
            implode(', ', $set),
            $where
        );
        
        $params = [];
        foreach ($data as $column => $value) {
            $params["set_$column"] = $value;
        }
        $params = array_merge($params, $whereParams);
        
        $stmt = $this->query($sql, $params);
        return $stmt->rowCount();
    }
    
    public function delete($table, $where, $params = []) {
        $sql = "DELETE FROM $table WHERE $where";
        $stmt = $this->query($sql, $params);
        return $stmt->rowCount();
    }
    
    public function beginTransaction() {
        return $this->pdo->beginTransaction();
    }
    
    public function commit() {
        return $this->pdo->commit();
    }
    
    public function rollback() {
        return $this->pdo->rollBack();
    }
    
    public function lastInsertId() {
        return $this->pdo->lastInsertId();
    }
    
    private function logQuery($sql, $params, $executionTime) {
        // Debug désactivé pour les requêtes SQL
        return;
    }
    
    private function logError($sql, $params, $error) {
        // On garde seulement le log d'erreur dans error_log, pas de debug
        error_log("SQL Error: $error | Query: $sql");
    }
    
    private function logConnection($success) {
        // Debug désactivé pour les connexions
        return;
    }
    
    /**
     * Récupère un enregistrement par ID de manière sécurisée
     */
    public function getById($table, $id, $columns = '*') {
        // Liste blanche des tables autorisées
        $allowedTables = [
            'clients', 'devis', 'devis_lignes', 'produits', 'marches', 
            'users', 'contacts', 'marches_listes', 'marches_produits',
            'devis_histo', 'medias', 'y_pages', 'z_logs', 'z_sessions'
        ];
        
        if (!in_array($table, $allowedTables)) {
            throw new Exception("Table non autorisée : $table");
        }
        
        $sql = "SELECT $columns FROM $table WHERE rowid = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->bindParam(':id', $id, PDO::PARAM_INT);
        $stmt->execute();
        
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    
    /**
     * Supprime un enregistrement par ID de manière sécurisée
     */
    public function deleteById($table, $id) {
        // Liste blanche des tables autorisées pour suppression
        $allowedTables = [
            'clients', 'devis', 'devis_lignes', 'contacts', 
            'devis_histo', 'medias', 'z_logs', 'z_sessions',
            'users', 'infos', 'marches', 'marches_listes', 'produits'
        ];
        
        if (!in_array($table, $allowedTables)) {
            throw new Exception("Suppression non autorisée sur la table : $table");
        }
        
        $sql = "DELETE FROM $table WHERE rowid = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->bindParam(':id', $id, PDO::PARAM_INT);
        
        return $stmt->execute();
    }
    
    /**
     * Recherche sécurisée dans un champ
     */
    public function searchByField($table, $field, $value, $orderBy = null) {
        // Liste blanche des tables et colonnes autorisées
        $allowedSearches = [
            'clients' => ['libelle', 'raison_sociale', 'ville', 'cp', 'email', 'contact_nom', 'contact_prenom'],
            'produits' => ['reference', 'designation', 'famille'],
            'devis' => ['num_devis', 'num_facture', 'opportunite'],
            'users' => ['username', 'firstname', 'lastname', 'email'],
            'marches' => ['libelle', 'description']
        ];
        
        if (!isset($allowedSearches[$table]) || !in_array($field, $allowedSearches[$table])) {
            throw new Exception("Recherche non autorisée : $table.$field");
        }
        
        $sql = "SELECT * FROM $table WHERE $field LIKE :value";
        
        if ($orderBy && in_array($orderBy, $allowedSearches[$table])) {
            $sql .= " ORDER BY $orderBy";
        }
        
        $stmt = $this->pdo->prepare($sql);
        $searchValue = '%' . $value . '%';
        $stmt->bindParam(':value', $searchValue, PDO::PARAM_STR);
        $stmt->execute();
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    // Fonction autocompleteSearch supprimée car non utilisée
    // L'autocomplétion est gérée côté client dans l'application
}

function getinfos($sql, $dbn = "gen", $format = "normal") {
    try {
        $db = Database::getInstance();
        $result = $db->fetchAll($sql);
        
        if (strtolower($format) == "json") {
            return json_encode($result);
        }
        return $result;
        
    } catch (Exception $e) {
        if ($_ENV['APP_DEBUG'] ?? false) {
            error_log("Erreur getinfos: " . $e->getMessage());
        }
        return ($format == "json") ? json_encode([]) : [];
    }
}

function qSQL($sql, $dbn = "gen", $lastid = false) {
    try {
        $db = Database::getInstance();
        
        $queryType = strtoupper(substr(trim($sql), 0, 6));
        
        if ($queryType === 'INSERT' || $queryType === 'UPDATE' || $queryType === 'DELETE') {
            $stmt = $db->query($sql);
            
            if ($lastid && $queryType === 'INSERT') {
                return $db->lastInsertId();
            }
            
            return $stmt;
        } else {
            return $db->query($sql);
        }
        
    } catch (Exception $e) {
        if ($_ENV['APP_DEBUG'] ?? false) {
            error_log("Erreur qSQL: " . $e->getMessage());
        }
        return false;
    }
}