<?php

namespace Ulmus\Adapter;

use Ulmus\{Common\Sql,
    ConnectionAdapter,
    Entity\InformationSchema\Table,
    Migration\FieldDefinition,
    Repository,
    QueryBuilder\SqlQueryBuilder,
    Ulmus};

trait SqlAdapterTrait
{
    public function repositoryClass() : string
    {
        return Repository::class;
    }

    public function queryBuilderClass() : string
    {
        return SqlQueryBuilder::class;
    }

    public function tableSyntax() : array
    {
        return [
            'ai' => "AUTO_INCREMENT",
            'pk' => "PRIMARY KEY",
            'unsigned' => "UNSIGNED",
        ];
    }

    public function databaseName() : string
    {
        return $this->database;
    }

    public function schemaTable(ConnectionAdapter $adapter, $databaseName, string $tableName) : null|object
    {
        return Table::repository(Repository::DEFAULT_ALIAS, $adapter)
            ->select(\Ulmus\Common\Sql::raw('this.*'))
            ->where($this->escapeIdentifier('table_schema', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
            ->loadOneFromField($this->escapeIdentifier('table_name', AdapterInterface::IDENTIFIER_FIELD), $tableName);
    }

    public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string
    {
        $mapper = new SqlFieldMapper($field);

        return $typeOnly ? $mapper->type : $mapper->render();
    }

    public function whitelistAttributes(array &$parameters) : void
    {
        $parameters = array_intersect_key($parameters, array_flip(static::ALLOWED_ATTRIBUTES));
    }

    public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable
    {
        if (! empty($field['previous']) ) {
            $position = sprintf('AFTER %s', $this->escapeIdentifier($field['previous']->field, AdapterInterface::IDENTIFIER_FIELD));
        }
        else {
            $position = "FIRST";
        }

        return implode(" ", [
            strtoupper($field['action']),
            $this->escapeIdentifier($definition->getSqlName(), AdapterInterface::IDENTIFIER_FIELD),
            $definition->getSqlType(),
            $definition->getSqlParams(),
            $position,
        ]);
    }

    public function writableValue(mixed $value) : mixed
    {
        switch (true) {
            case $value instanceof \UnitEnum:
                return Ulmus::convertEnum($value);

            case is_object($value):
                return Ulmus::convertObject($value);

            case is_array($value):
                return json_encode($value);

            case is_bool($value):
                return (int) $value;
        }

        return $value;
    }
    
    public function splitAlterQuery() : bool
    {
        return false;
    }
}