<?php

declare(strict_types=1);

namespace PpPhp\Core\Database\Schema;

use PpPhp\Core\Database\Dialect\DatabaseDialectInterface;

/**
 * Engine-neutral orchestration boundary for the Tables module.
 *
 * Controllers deal only with validated schema models; every SQL decision is
 * delegated to the active dialect adapter.
 */
final class TableDesignerService
{
    public function __construct(
        private readonly \PDO $pdo,
        private readonly DatabaseDialectInterface $dialect,
        private readonly array $config,
        private readonly FieldDefinitionNormalizer $normalizer = new FieldDefinitionNormalizer()
    ) {}

    public function dialectConfig(): array
    {
        return $this->dialect->uiConfig();
    }

    public function listObjects(): array
    {
        return $this->dialect->listObjects($this->pdo, $this->config);
    }

    public function describe(string $table): array
    {
        return $this->dialect->describeTable($this->pdo, $this->normalizer->assertTableName($table), $this->config);
    }

    public function create(string $table, array $rawFields): array
    {
        $table = $this->normalizer->assertTableName($table);
        $fields = $this->normalizer->normalize($rawFields, $this->dialect);
        $this->dialect->createTable($this->pdo, $table, $fields, $this->config);
        return $fields;
    }

    public function update(string $oldTable, string $newTable, array $rawFields): array
    {
        $oldTable = $this->normalizer->assertTableName($oldTable);
        $newTable = $this->normalizer->assertTableName($newTable);
        $fields = $this->normalizer->normalize($rawFields, $this->dialect);
        $details = $this->dialect->describeTable($this->pdo, $oldTable, $this->config);
        $this->dialect->updateTable($this->pdo, $oldTable, $newTable, $fields, $details, $this->config);
        return $fields;
    }

    public function drop(string $name, ?string $type = null): void
    {
        $name = $this->normalizer->assertTableName($name);
        if ($type === null) {
            foreach ($this->listObjects() as $object) {
                if ((string)$object['name'] === $name) {
                    $type = (string)$object['type'];
                    break;
                }
            }
        }
        $this->dialect->dropObject($this->pdo, $name, $type ?? 'TABLE', $this->config);
    }

    public function createView(string $name, string $selectSql, bool $replace): void
    {
        $name = $this->normalizer->assertTableName($name);
        $this->dialect->createView($this->pdo, $name, $selectSql, $replace, $this->config);
    }

    public function preview(string $selectSql, int $limit = 10): array
    {
        return $this->dialect->preview($this->pdo, $selectSql, $limit);
    }

    public function addForeignKey(array $relation): void
    {
        $this->dialect->addForeignKey($this->pdo, $this->normalizeRelation($relation), $this->config);
    }

    public function dropForeignKey(array $relation): void
    {
        $this->dialect->dropForeignKey($this->pdo, $this->normalizeRelation($relation), $this->config);
    }

    public function countRows(string $table): int
    {
        return $this->dialect->countRows($this->pdo, $this->normalizer->assertTableName($table), $this->config);
    }

    private function normalizeRelation(array $relation): array
    {
        foreach (['master_table', 'detail_table', 'master_field', 'detail_field'] as $key) {
            $relation[$key] = $this->normalizer->assertTableName((string)($relation[$key] ?? ''));
        }
        if (!empty($relation['constraint_name'])) {
            $relation['constraint_name'] = $this->normalizer->assertTableName((string)$relation['constraint_name']);
        }
        return $relation;
    }
}
