<?php

namespace App\Traits;

use App\Tools\CustomColumnTool;

trait IsDynamicSchema
{
    /**
     * @param CustomColumnTool $tool The custom column tool instance.
     */
    public function __construct(public CustomColumnTool $tool = new CustomColumnTool())
    {
    }

    /**
     * Get the latest version of the schema
     *
     * @return mixed
     */
    public static function latestVersion()
    {
        return self::latest()->firstOrNew();
    }


    /**
     * Get the schema as an array
     *
     * @return array
     */
    public function getSchemaAttribute(): array
    {
        $schema = $this->attributes['schema'] ?? [];

        if (is_string($schema)) {
            return json_decode($schema, true) ?? [];
        }

        return $schema;
    }


    /**
     * Add a custom column to the schema.
     *
     * @param string $name The name of the column.
     * @param string $description The description of the column.
     * @param string $column_type The type of the column.
     * @param array $options Additional options for the column.
     * @return void
     */
    public function addColumn(string $name, string $description, string $column_type, array $options = []): void
    {
        $schema = $this->schema;

        if (array_key_exists($name, $schema)) {
            $suffix = 2;
            $originalName = $name;
            while (array_key_exists($name, $schema)) {
                $name = $originalName . '_' . $suffix;
                $suffix++;
            }
        }

        switch ($column_type) {
            case "string":
            case "boolean":
            case "text":
            case "email":
            case "tel":
            case "integer":
            case "decimal":
            case "date":
            case "time":
            case "datetime":
            case "file":
            case "list":
                $schema[$name] = $this->tool->generateCustomColumn($description, $column_type, $options);
                $this->setAttribute('schema', $schema);
                break;
            case "enum":
            case "enum_single":
                $schema[$name] = $this->tool->generateEnumColumn($description, $column_type, $options);
                $this->setAttribute('schema', $schema);
                break;
        }
    }


    public function addColumnV2(string $name, string $description, string $column_type, array $options = []): void
    {
        $options['required'] = $options['required'] ?? true;
        $options['system_default'] = $options['system_default'] ?? true;
        $schema = $this->schema;

        if (array_key_exists($name, $schema)) {
            $suffix = 2;
            $originalName = $name;
            while (array_key_exists($name, $schema)) {
                $name = $originalName . '_' . $suffix;
                $suffix++;
            }
        }

        switch ($column_type) {
            case "string":
            case "boolean":
            case "text":
            case "email":
            case "tel":
            case "integer":
            case "decimal":
            case "date":
            case "time":
            case "datetime":
            case "file":
            case "base64file":
            case "list":
                $schema[$name] = $this->tool->generateCustomColumn($description, $column_type, $options);
                $this->setAttribute('schema', $schema);
                break;
            case "enum":
            case "enum_single":
                $schema[$name] = $this->tool->generateEnumColumn($description, $column_type, $options);
                $this->setAttribute('schema', $schema);
                break;
        }
    }

    /**
     * Add a foreign key column to the schema.
     *
     * @param string $name The name of the column.
     * @param string $description The description of the column.
     * @param string $referenced_table The referenced table for the foreign key.
     * @param string $referenced_column The referenced column in the referenced table.
     * @param array $options Additional options for the column.
     * @return void
     */
    public function addForeignKey(string $name, string $description, string $referenced_table, string $referenced_column = 'id', array $options = []): void
    {
        $schema = $this->schema;

        if (array_key_exists($name, $schema)) {
            $suffix = 2;
            $originalName = $name;
            while (array_key_exists($name, $schema)) {
                $name = $originalName . '_' . $suffix;
                $suffix++;
            }
        }

        $options['relationship_column'] = $name . '_id';
        $schema[$name] = $this->tool->generateForeignKey($description, $referenced_table, $referenced_column, $options);
        $this->setAttribute('schema', $schema);
    }

    /**
     * Add a multi foreign key reference column to the schema.
     *
     * @param string $name The name of the column.
     * @param string $description The description of the column.
     * @param string $referenced_table The referenced table for the foreign key.
     * @param string $referenced_column The referenced column in the referenced table.
     * @param array $options Additional options for the column.
     * @return void
     */
    public function addMultiForeignKey(string $name, string $description, string $referenced_table, string $referenced_column = 'id', array $options = []): void
    {
        $schema = $this->schema;

        if (array_key_exists($name, $schema)) {
            $suffix = 2;
            $originalName = $name;
            while (array_key_exists($name, $schema)) {
                $name = $originalName . '_' . $suffix;
                $suffix++;
            }
        }

        $schema[$name] = $this->tool->generateMultiForeignKey($description, $referenced_table, $referenced_column, $options);
        $this->setAttribute('schema', $schema);
    }


    public function addForeignKeyV2(string $name, string $description, string $referenced_table, string $referenced_column = 'id', array $options = []): void
    {
        $options['required'] = $options['required'] ?? true;
        $options['system_default'] = $options['system_default'] ?? true;
        $schema = $this->schema;

        if (array_key_exists($name, $schema)) {
            $suffix = 2;
            $originalName = $name;
            while (array_key_exists($name, $schema)) {
                $name = $originalName . '_' . $suffix;
                $suffix++;
            }
        }

        //        $last_string = last(explode("_id",$name));
//        $name = empty($last_string) ? $name :  $name . '_id';
        $options['relationship_column'] = $name;
        $schema[$name] = $this->tool->generateForeignKey($description, $referenced_table, $referenced_column, $options);
        $this->setAttribute('schema', $schema);
    }

    /**
     * Get the list of columns in the schema.
     *
     * @return array The list of columns.
     */
    public function getColumns(): array
    {
        return array_keys($this->schema);
    }

    /**
     * Check if a column exists in the schema.
     *
     * @param string $name The name of the column.
     * @return bool True if the column exists, false otherwise.
     */
    public function hasColumn(string $name): bool
    {
        return array_key_exists($name, $this->schema);
    }

    /**
     * Check if a foreign key column exists in the schema.
     *
     * @param string $name The name of the column.
     * @return bool True if the foreign key column exists, false otherwise.
     */
    public function hasForeign(string $name): bool
    {

        $columns = $this->schema;
        foreach ($columns as $column_name => $column_data) {
            if ($column_name === $name || ($column_data['type'] === 'foreign_key' && $column_data['options']['relationship_column'] === $name)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if a column is a custom column (not system default).
     *
     * @param string $name The name of the column.
     * @return bool True if the column is custom, false otherwise.
     */
    public function hasCustomColumn(string $name): bool
    {
        return $this->hasColumn($name) && !$this->schema[$name]['system_default'];
    }

    /**
     * Check if a foreign key column is a custom column (not system default).
     *
     * @param string $name The name of the column.
     * @return bool True if the foreign key column is custom, false otherwise.
     */
    public function hasCustomForeignColumn(string $name): bool
    {
        $columns = $this->schema;
        foreach ($columns as $column_name => $column_data) {
            if ($column_data['type'] === 'foreign_key' && $column_data['options']['relationship_column'] === $name) {
                return !$column_data['system_default'];
            }
        }

        return false;
    }

    /**
     * Check if a column is custom (not system default).
     *
     * @param string $name The name of the column.
     * @return bool True if the column is custom, false otherwise.
     */
    public function isCustom(string $name): bool
    {
        return $this->hasCustomColumn($name) || $this->hasCustomForeignColumn($name);
    }

    /**
     * Remove a column from the schema.
     *
     * @param string $column_name The name of the column to remove.
     * @return void
     */
    public function removeColumn(string $column_name): void
    {
        $schema = $this->schema;

        if (array_key_exists($column_name, $schema)) {
            unset($schema[$column_name]);
            $this->schema = $schema;
        }
    }

    /**
     * Change the description of a column in the schema.
     *
     * @param string $column_name The name of the column.
     * @param string $new_description The new description for the column.
     * @return void
     */
    public function changeColumnDescription(string $column_name, string $new_description): void
    {
        $schema = $this->schema;

        if (array_key_exists($column_name, $schema)) {
            $schema[$column_name]['description'] = $new_description;
            $this->schema = $schema;
        }
    }

    /**
     * Toggle the required status of a column in the schema.
     *
     * @param string $name The name of the column.
     * @param bool $isRequired Whether the column should be required or not.
     * @return void
     */
    public function toggleColumnRequiredStatus(string $name, bool $isRequired): void
    {
        if ($this->hasColumn($name)) {
            $schema = $this->schema;
            $schema[$name]['options']['required'] = $isRequired;
            $this->schema = $schema;
        }
    }

    /**
     * Save all changes in database
     *
     * @return void
     */
    public function saveChanges(): void
    {
        $this->schema = json_encode($this->schema);
        $this->save();
    }

    /**
     * Generate a new instance from the current unsaved instance
     *
     * @return mixed The new schema instance.
     */
    public function regenerate()
    {
        $new_schema = $this->replicate();
        $new_schema->saveChanges();

        return $new_schema;
    }
}

