<?php

namespace CliqThemes\ORM\Schema;

use CliqThemes\ORM\Database\Connection;

/**
 * Coordinates creating, updating and dropping database tables using blueprints.
 */
class SchemaManager
{
    protected Connection $connection;

    /**
     * Create a new schema manager optionally using a custom connection.
     */
    public function __construct(?Connection $connection = null)
    {
        $this->connection = $connection ?? new Connection();
    }

    /**
     * Create a new table by executing the provided blueprint callback.
     */
    public function createTable(string $table, callable $callback): void
    {
        $this->build($table, $callback);
    }

    /**
     * Update an existing table using the blueprint callback.
     */
    public function updateTable(string $table, callable $callback): void
    {
        $this->build($table, $callback);
    }

    /**
     * Drop the given table if it exists.
     */
    public function dropTable(string $table): void
    {
        $qualified = $this->connection->table($table);
        $this->connection->statement(sprintf('DROP TABLE IF EXISTS `%s`', $qualified));
    }

    /**
     * Determine if the specified table exists in the database.
     */
    public function tableExists(string $table): bool
    {
        $qualified = $this->connection->table($table);

        $sql = "SHOW TABLES LIKE %s";
        $result = $this->connection->select($sql, [$qualified]);

        return !empty($result);
    }

    /**
     * Build a table blueprint and execute it through dbDelta.
     */
    protected function build(string $table, callable $callback): void
    {
        $blueprint = new TableBlueprint($this->connection, $table);
        $callback($blueprint);

        $this->runBlueprint($blueprint);
    }

    /**
     * Execute the SQL generated by the blueprint.
     *
     * Tables that declare POINT columns or SPATIAL indexes are created with a
     * direct CREATE TABLE IF NOT EXISTS statement because dbDelta cannot parse
     * those definitions. All other tables continue to flow through dbDelta so
     * existing columns are diffed and added in place on upgrades.
     */
    protected function runBlueprint(TableBlueprint $blueprint): void
    {
        if ($blueprint->hasSpatialFeatures()) {
            $this->connection->statement($blueprint->toSql(true));

            return;
        }

        if (!function_exists('dbDelta')) {
            if (!defined('ABSPATH')) {
                throw new \RuntimeException('ABSPATH is not defined. Ensure WordPress is bootstrapped before running schema changes.');
            }

            require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        }

        dbDelta($blueprint->toSql());
    }
}

