<?php
/**
 * Base ORM Grammar Class
 *
 * @package ZenoChatbot\Framework\Database\ORM\Grammar
 */

namespace ZenoChatbot\Framework\Database\ORM\Grammar;

use ZenoChatbot\Framework\Database\ORM\Connection;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Abstract Base ORM Grammar Class
 */
abstract class Grammar
{
    /**
     * Database connection
     *
     * @var Connection
     */
    protected $connection;

    /**
     * Create a new grammar instance
     *
     * @param Connection $connection
     */
    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Compile a select statement
     *
     * @param array $components
     * @return string
     */
    abstract public function compileSelect(array $components);

    /**
     * Compile an insert statement
     *
     * @param string $table
     * @param array $values
     * @return string
     */
    abstract public function compileInsert($table, array $values);

    /**
     * Compile an update statement
     *
     * @param string $table
     * @param array $values
     * @param array $wheres
     * @return string
     */
    abstract public function compileUpdate($table, array $values, array $wheres);

    /**
     * Compile a delete statement
     *
     * @param string $table
     * @param array $wheres
     * @return string
     */
    abstract public function compileDelete($table, array $wheres);

    /**
     * Compile where clauses
     *
     * @param array $wheres
     * @return string
     */
    abstract public function compileWheres(array $wheres);

    /**
     * Get full table name with prefix
     *
     * @param string $table
     * @return string
     */
    protected function getTableName($table)
    {
        return $this->connection->getPrefix() . $table;
    }

    /**
     * Wrap value in quotes if it's a string
     *
     * @param mixed $value
     * @return string
     */
    protected function wrapValue($value)
    {
        // Nulls
        if (is_null($value)) {
            return 'NULL';
        }

        // Booleans
        if (is_bool($value)) {
            return $value ? '1' : '0';
        }

        // DateTime -> formatted and quoted
        if ($value instanceof \DateTime) {
            return "'" . $value->format('Y-m-d H:i:s') . "'";
        }

        // Numbers (int/float) should not be quoted
        if (is_int($value) || is_float($value)) {
            return (string)$value;
        }

        // Strings: quote using wpdb->prepare to ensure proper escaping
        if (is_string($value)) {
            global $wpdb;
            if ($wpdb instanceof \wpdb) {
                // prepare('%s', $value) returns the value already quoted and properly escaped
                return $wpdb->prepare('%s', $value);
            }
            // Fallback to esc_sql and manual quoting if $wpdb is not available
            $escaped = function_exists('esc_sql') ? \esc_sql($value) : addslashes($value);
            return "'{$escaped}'";
        }

        // Fallback: cast to string and quote safely
        $stringValue = (string)$value;
        $escaped = function_exists('esc_sql') ? \esc_sql($stringValue) : addslashes($stringValue);
        return "'{$escaped}'";
    }

    /**
     * Wrap identifier in backticks
     *
     * @param string $identifier
     * @return string
     */
    protected function wrapIdentifier($identifier)
    {
        // Don't wrap wildcard
        if ($identifier === '*') {
            return '*';
        }

        // Don't wrap function calls like COUNT(*), MAX(id), etc.
        if (preg_match('/^[A-Z_]+\s*\(/i', $identifier)) {
            return $identifier;
        }

        // Handle table.column format
        if (strpos($identifier, '.') !== false) {
            $parts = explode('.', $identifier);
            return implode('.', array_map(function ($part) {
                return $part === '*' ? '*' : "`{$part}`";
            }, $parts));
        }

        return "`{$identifier}`";
    }

    /**
     * Get database type
     *
     * @return string
     */
    protected function getDatabaseType()
    {
        return $this->connection->getType();
    }

    /**
     * Check if database is MySQL
     *
     * @return bool
     */
    protected function isMysql()
    {
        return $this->connection->isMysql();
    }

    /**
     * Check if database is SQLite
     *
     * @return bool
     */
    protected function isSqlite()
    {
        return $this->connection->isSqlite();
    }
}
