<?php

declare(strict_types=1);

namespace App\Framework\Database\Schema;

use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Platform\DatabasePlatform;
use App\Framework\Database\Platform\ValueObjects\ColumnDefinition;
use App\Framework\Database\Platform\ValueObjects\IndexDefinition;
use App\Framework\Database\Platform\ValueObjects\TableOptions;

/**
 * Schema builder for database-independent table creation
 */
final readonly class SchemaBuilder
{
    public function __construct(
        private DatabasePlatform $platform,
        private ConnectionInterface $connection
    ) {
    }

    public function createTable(string $tableName, array $columns, ?TableOptions $options = null): void
    {
        $tableOptions = $options ?? TableOptions::default();

        $sql = $this->platform->getCreateTableSQL($tableName, $columns, $tableOptions);
        $this->connection->execute($sql);
    }

    public function dropTable(string $tableName, bool $ifExists = true): void
    {
        $sql = $this->platform->getDropTableSQL($tableName, $ifExists);
        $this->connection->execute($sql);
    }

    public function tableExists(string $tableName): bool
    {
        $sql = $this->platform->getTableExistsSQL($tableName);
        $result = $this->connection->queryColumn($sql, [$tableName]);

        return ! empty($result) && (int) $result[0] > 0;
    }

    public function createIndex(string $tableName, IndexDefinition $index): void
    {
        $sql = $this->platform->getCreateIndexSQL(
            $tableName,
            $index->name,
            $index->columns,
            ['type' => $index->type]
        );

        $this->connection->execute($sql);
    }

    public function listTables(): array
    {
        $sql = $this->platform->getListTablesSQL();

        return $this->connection->queryColumn($sql);
    }

    /**
     * Builder pattern for table creation
     */
    public function table(string $tableName): TableBuilder
    {
        return new TableBuilder($this, $tableName);
    }
}

/**
 * Fluent table builder for easy table creation
 */
final class TableBuilder
{
    private array $columns = [];

    private array $indexes = [];

    private ?TableOptions $options = null;

    public function __construct(
        private readonly SchemaBuilder $schemaBuilder,
        private readonly string $tableName
    ) {
    }

    public function id(string $name = 'id'): self
    {
        $this->columns[] = ColumnDefinition::id($name);

        return $this;
    }

    public function string(string $name, int $length = 255, bool $nullable = true): self
    {
        $this->columns[] = ColumnDefinition::string($name, $length, $nullable);

        return $this;
    }

    public function text(string $name, bool $nullable = true): self
    {
        $this->columns[] = ColumnDefinition::text($name, $nullable);

        return $this;
    }

    public function integer(string $name, bool $nullable = true, bool $unsigned = false): self
    {
        $this->columns[] = ColumnDefinition::integer($name, $nullable, $unsigned);

        return $this;
    }

    public function bigInteger(string $name, bool $nullable = true, bool $unsigned = false): self
    {
        $this->columns[] = ColumnDefinition::bigInteger($name, $nullable, $unsigned);

        return $this;
    }

    public function decimal(string $name, int $precision = 8, int $scale = 2, bool $nullable = true): self
    {
        $this->columns[] = ColumnDefinition::decimal($name, $precision, $scale, $nullable);

        return $this;
    }

    public function boolean(string $name, bool $nullable = true, ?bool $default = null): self
    {
        $this->columns[] = ColumnDefinition::boolean($name, $nullable, $default);

        return $this;
    }

    public function timestamp(string $name, bool $nullable = true): self
    {
        $this->columns[] = ColumnDefinition::timestamp($name, $nullable);

        return $this;
    }

    public function timestamps(): self
    {
        $timestamps = ColumnDefinition::timestamps();
        foreach ($timestamps as $timestamp) {
            $this->columns[] = $timestamp;
        }

        return $this;
    }

    public function json(string $name, bool $nullable = true): self
    {
        $this->columns[] = ColumnDefinition::json($name, $nullable);

        return $this;
    }

    public function uuid(string $name, bool $nullable = true): self
    {
        $this->columns[] = ColumnDefinition::uuid($name, $nullable);

        return $this;
    }

    public function column(ColumnDefinition $column): self
    {
        $this->columns[] = $column;

        return $this;
    }

    public function index(IndexDefinition $index): self
    {
        $this->indexes[] = $index;

        return $this;
    }

    public function unique(string $name, array $columns): self
    {
        $this->indexes[] = IndexDefinition::unique($name, $columns);

        return $this;
    }

    public function foreignKey(string $column, string $referencedTable, string $referencedColumn = 'id'): self
    {
        // Foreign keys will be handled in a separate method/builder
        return $this;
    }

    public function engine(string $engine): self
    {
        $this->options = ($this->options ?? TableOptions::default())->withCustomOption('engine', $engine);

        return $this;
    }

    public function charset(string $charset, ?string $collation = null): self
    {
        $this->options = ($this->options ?? TableOptions::default())->withCharset($charset, $collation);

        return $this;
    }

    public function comment(string $comment): self
    {
        $this->options = ($this->options ?? TableOptions::default())->withComment($comment);

        return $this;
    }

    public function temporary(): self
    {
        $this->options = TableOptions::temporary();

        return $this;
    }

    public function create(): void
    {
        if (empty($this->columns)) {
            throw new \RuntimeException("Cannot create table '{$this->tableName}' without columns");
        }

        $this->schemaBuilder->createTable($this->tableName, $this->columns, $this->options);

        // Create additional indexes
        foreach ($this->indexes as $index) {
            $this->schemaBuilder->createIndex($this->tableName, $index);
        }
    }
}
