<?php

namespace App\Tools;

use Illuminate\Support\Facades\Schema;


class CustomColumnTool
{
    protected $column_types = [
        ['name' => 'string', 'description' => 'Short answer'],
        ['name' => 'text', 'description' => 'Long answer'],
        ['name' => 'email', 'description' => 'Email'],
        ['name' => 'tel', 'description' => 'Phone Number'],
        ['name' => 'integer', 'description' => 'Number'],
        ['name' => 'decimal', 'description' => 'Decimal'],
        ['name' => 'date', 'description' => 'Date'],
        ['name' => 'time', 'description' => 'Time'],
        ['name' => 'datetime', 'description' => 'Date & Time'],
        ['name' => 'boolean', 'description' => 'Checkbox'],
        ['name' => 'file', 'description' => 'File upload'],
        ['name' => 'base64file', 'description' => 'Base64 File upload'],
        ['name' => 'enum', 'description' => 'Multiple choice list'],
        ['name' => 'enum_single', 'description' => 'Single choice list'],
        ['name' => 'foreign_key', 'description' => 'Reference to another data set'],
        ['name' => 'multi_foreign_key', 'description' => 'Multiple references to a data set'],
        ['name' => 'list', 'description' => 'List of values'],
    ];


    protected $column_options = [
        [
            "name" => "required",
            "description" => "Mandatory/Optional",
            "value_type" => "boolean",
            "default" => "true"
        ],
        [
            "name" => "max",
            "description" => "Maximum length/Value",
            "value_type" => "integer",
            "default" => "null"
        ],
        [
            "name" => "default_value",
            "description" => "Default Value",
            "value_type" => "string",
            "default" => "null"
        ],
        [
            "name" => "answers",
            "description" => "Answers",
            "value_type" => "string",
            "default" => "null"
        ],
        [
            "name" => "notes",
            "description" => "Additional Notes",
            "value_type" => "string",
            "default" => "null"
        ]
    ];

    /**
     * Generate data structure for a custom column
     * @param string $column_name
     * @param string $column_type
     * @param array $options
     * @return array
     */
    public function generateCustomColumn(string $description, string $column_type, array $options = []): array
    {
        $this->validateCustomColumn($column_type, $options);

        $return = [
            "description" => $description,
            "type" => $column_type,
            "system_default" => $options["system_default"] ?? false,
            "options" => [
                "required" => $options["required"] ?? true,
                "max" => $options["max"] ?? null,
                "default_value" => $options["default_value"] ?? null,
            ]
        ];

        if (isset($options["referenced_table"], $options['referenced_column'], $options['relationship_column'])) {
            $return["options"]["referenced_table"] = $options["referenced_table"];
            $return["options"]["referenced_column"] = $options["referenced_column"];
            $return["options"]["relationship_column"] = $options["relationship_column"];

            if (!isset($options['central'])) {
                $options["referenced_table"] = $options["referenced_table"] == 'staffs' ? 'staff' : $options["referenced_table"];
                $this->validateForeignKeyOptions($options["referenced_table"], $options['referenced_column']);
            }
        } elseif (isset($options["optional_tables"])) {
            $return["options"]["optional_tables"] = $options["optional_tables"];
        }

        return $return;
    }


    /**
     * Generate data structure for a custom enum column
     * @param string $column_name
     * @param string $column_type
     * @param array $options
     * @return array
     */
    public function generateEnumColumn(string $description, string $column_type, array $options = []): array
    {
        $this->validateCustomColumn($column_type, $options);

        $answers = $options["answers"] ?? [];
        $formatted_answers = [];

        $key = 1;
        foreach ($answers as $answer) {
            $formatted_answers["answer_" . $key++] = $answer;
        }

        return [
            "description" => $description,
            "type" => $column_type,
            "system_default" => $options["system_default"] ?? false,
            "options" => [
                    "required" => $options["required"] ?? true,
                    "answers" => $formatted_answers
                ]
        ];
    }


    /**
     * Generate the structure for a foreign key reference column
     * @param string $description
     * @param string $table_name
     * @param string $column_name
     * @param array $columns
     * @return array
     */
    public function generateForeignKey(string $description, string $referenced_table, string $referenced_column = 'id', array $options = []): array
    {
        $is_foreign_key_list = $options["foreign_key_list"] ?? false;
        if(!isset($options['central'])){
            $this->validateForeignKeyOptions($referenced_table, $referenced_column);
        }
        return [
            "description" => $description,
            "type" => $is_foreign_key_list ? "foreign_key_list" : "foreign_key",
            "system_default" => $options["system_default"] ?? false,
            "options" => [
                "referenced_table" => $referenced_table,
                "referenced_column" => $referenced_column,
                "relationship_column" => $options["relationship_column"],
                "required" => $options["required"] ?? true,
                "central" => $options["central"] ?? false,
            ]
        ];
    }

    /**
     * Generate the structure for a multi foreign key reference column
     * @param string $description
     * @param string $table_name
     * @param string $column_name
     * @param array $columns
     * @return array
     */
    public function generateMultiForeignKey(string $description, string $referenced_table, string $referenced_column = 'id', array $options = []): array
    {
        if (!isset($options['central'])) {
            $this->validateForeignKeyOptions($referenced_table, $referenced_column);
        }
        return [
            "description" => $description,
            "type" => "multi_foreign_key",
            "system_default" => $options["system_default"] ?? false,
            "options" => [
                    "referenced_table" => $referenced_table,
                    "referenced_column" => $referenced_column,
                    "required" => $options["required"] ?? true,
                    "central" => $options["central"] ?? false,
                ]
        ];
    }


    /**
     * Validate options for a custom column
     * @param string $type
     * @param array $options
     * @throws \InvalidArgumentException
     */
    public function validateCustomColumn(string $type, array $options): void
    {
        $this->validateColumnType($type);
        $this->validateOptionTypes($options);

        switch ($type) {
            case 'enum':
            case 'enum_single':
                $this->validateEnumAnswers($options);
                break;
        }
    }


    /**
     * Validate the column type
     * @param string $type
     * @throws \InvalidArgumentException
     */
    public function validateColumnType(string $type): void
    {
        $validTypes = collect($this->column_types)->pluck('name')->toArray();
        if (!in_array($type, $validTypes)) {
            throw new \InvalidArgumentException("Invalid column type: $type");
        }
    }


    /**
     * Validate option types
     * @param array $options
     * @throws \InvalidArgumentException
     */
    public function validateOptionTypes(array $options): void
    {
        $expected_types = [
            'required' => 'boolean',
            'max' => 'integer',
            'default_value' => 'string',
            'answers' => 'array',
        ];

        foreach ($expected_types as $option => $expected_type) {
            if (isset($options[$option]) && gettype($options[$option]) !== $expected_type) {
                throw new \InvalidArgumentException("Invalid value type for '$option' option: $expected_type expected");
            }
        }
    }


    /**
     * Validate 'answers' field for enum columns
     * @param array $options
     * @throws \InvalidArgumentException
     */
    public function validateEnumAnswers(array $options): void
    {
        if (!isset($options['answers']) || !is_array($options['answers'])) {
            throw new \InvalidArgumentException("Invalid 'answers' field for enum column: it must be an array of strings");
        }
        foreach ($options['answers'] as $answer) {
            if (!is_string($answer)) {
                throw new \InvalidArgumentException("Invalid answer in 'answers' field for enum column: $answer");
            }
        }
    }

    /**
     * Validate options for a foreign key
     * @param string $referenced_table
     * @param string $referenced_column
     * @throws \InvalidArgumentException
     */
    public function validateForeignKeyOptions(string $referenced_table, string $referenced_column): void
    {
        if (!Schema::hasTable($referenced_table)) {
            throw new \InvalidArgumentException("Referenced table '$referenced_table' does not exist");
        }

        if (!Schema::hasColumn($referenced_table, $referenced_column)) {
            throw new \InvalidArgumentException("Referenced column '$referenced_column' does not exist in table '$referenced_table'");
        }
    }

    /**
     * Generate a name from a description
     * @param string $description
     * @return string
     */
    public static function generateName(string $description): string
    {
        $name = preg_replace('/[^a-zA-Z0-9\s]/', '', $description);
        $name = preg_replace('/\s+/', '_', $name);
        if (preg_match('/^\d/', $name)) {
            $name = '_' . $name;
        }
        return strtolower($name);
    }


    /**
     * Get tables that can be referenced as foreign keys
     * @return array
     */
    public function getReferencableTables(): array
    {
        return [
            [
                "name" => "staff",
                "description" => "Staff"
            ],
            [
                "name" => "positions",
                "description" => "Positions"
            ],
            [
                "name" => "departments",
                "description" => "Departments"
            ],
            [
                "name" => "marital_statuses",
                "description" => "Marital statuses"
            ],
            [
                "name" => "genders",
                "description" => "Genders"
            ],
            [
                "name" => "users",
                "description" => "Users"
            ],
            [
                "name" => "employment_types",
                "description" => "Employment Types"
            ]
        ];
    }

    /**
     * Get the array of available column types
     * @param $with_description = false
     * @return array
     */
    public function getColumnTypes($with_description = false): array
    {
        if ($with_description) {
            return $this->column_types;
        }
        return array_column($this->column_types, 'name');
    }

    /**
     * Get the array of available column creation options
     *
     * @return array
     */
    public function getColumnOptions(): array
    {
        return $this->column_options;
    }

}
