<?php

namespace App\Traits;

use App\Tools\CustomColumnTool;

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

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

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

        return $structure;
    }

    /**
     * Get the latest structure definition.
     *
     * @return mixed
     */
    public static function currentStructure()
    {
        return self::latest()->firstOrNew();
    }

    /**
     * Add a custom column to the structure.
     *
     * @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
    {
        $structure = $this->structure;

        if (array_key_exists($name, $structure)) {
            $suffix = 2;
            $originalName = $name;
            while (array_key_exists($name, $structure)) {
                $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":
                $structure[$name] = $this->tool->generateCustomColumn($description, $column_type, $options);
                $this->setAttribute('structure', $structure);
                break;
            case "enum":
            case "enum_single":
                $structure[$name] = $this->tool->generateEnumColumn($description, $column_type, $options);
                $this->setAttribute('structure', $structure);
                break;
        }
    }

    /**
     * Add a foreign key column to the structure.
     *
     * @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
    {
        $structure = $this->structure;

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

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

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

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

    /**
     * Check if a foreign key column exists in the structure.
     *
     * @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
    {

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

        return false;
    }

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

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

    /**
     * Change the description of a column.
     *
     * @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
    {
        $structure = $this->structure;

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

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