<?php

namespace PublishPress\FuturePro\Modules\Workflows\DBTableSchemas;

use PublishPress\Future\Framework\Database\Interfaces\DBTableSchemaHandlerInterface;
use PublishPress\Future\Framework\Database\Interfaces\DBTableSchemaInterface;

defined('ABSPATH') or die('Direct access not allowed.');

class EventDrivenActionsSchema implements DBTableSchemaInterface
{
    public const HEALTH_ERROR_TABLE_DOES_NOT_EXIST = 'table_does_not_exist';
    public const HEALTH_ERROR_INVALID_INDEX = 'invalid_index';
    public const HEALTH_ERROR_INVALID_COLUMN = 'invalid_column';

    /**
     * @var DBTableSchemaHandlerInterface
     */
    private $handler;

    public function __construct(DBTableSchemaHandlerInterface $handler, string $tableName)
    {
        $this->handler = $handler;
        $this->handler->setTableName($tableName);
    }

    public function getTableName(): string
    {
        return $this->handler->getTableName();
    }

    private function getColumns(): array
    {
        return [
            'id' => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT',
            'user_id' => 'bigint(20) UNSIGNED NOT NULL',
            'workflow_id' => 'bigint(20) UNSIGNED NULL',
            'step_id' => 'varchar(100) NOT NULL',
            'created_at' => 'datetime NOT NULL DEFAULT CURRENT_TIMESTAMP',
            'status' => 'varchar(100) NOT NULL DEFAULT "pending"',
            'error' => 'text NULL',
            'started_at' => 'datetime NULL',
            'completed_at' => 'datetime NULL',
            'args' => 'text NULL',
        ];
    }

    private function getIndexes(): array
    {
        return [
            'PRIMARY' => ['id'],
            'user_workflow' => ['user_id', 'workflow_id'],
            'step' => ['workflow_id', 'step_id'],
            'status' => ['status'],
        ];
    }

    public function createTable(): bool
    {
        return $this->handler->createTable($this->getColumns(), $this->getIndexes());
    }

    public function dropTable(): bool
    {
        return $this->handler->dropTable();
    }

    public function isTableHealthy(): bool
    {
        $this->handler->resetErrors();

        if (! $this->isTableExistent()) {
            $tablePrefix = $this->handler->getTablePrefix();

            $this->handler->registerError(
                self::HEALTH_ERROR_TABLE_DOES_NOT_EXIST,
                sprintf(
                    __(
                        'The table %s does not exist.',
                        'post-expirator'
                    ),
                    $tablePrefix . $this->getTableName()
                )
            );

            // Table do not exists, we don't need to check columns and indexes.
            return false;
        }

        $columnsErrors = $this->handler->checkTableColumns($this->getColumns());
        if (! empty($columnsErrors)) {
            foreach ($columnsErrors as $columnError) {
                $this->handler->registerError(
                    self::HEALTH_ERROR_INVALID_COLUMN,
                    $columnError
                );
            }
        }

        $indexesErrors = $this->handler->checkTableIndexes($this->getIndexes());
        if (! empty($indexesErrors)) {
            foreach ($indexesErrors as $indexError) {
                $this->handler->registerError(
                    self::HEALTH_ERROR_INVALID_INDEX,
                    $indexError
                );
            }
        }

        return false === $this->handler->hasErrors();
    }

    public function isTableExistent(): bool
    {
        return $this->handler->isTableExistent();
    }

    public function getErrors(): array
    {
        return $this->handler->getErrors();
    }

    public function fixTable(): void
    {
        if (! $this->isTableExistent()) {
            $this->createTable();
        }

        if (! empty($this->handler->checkTableColumns($this->getColumns()))) {
            $this->handler->fixColumns($this->getColumns());
        }

        if (! empty($this->handler->checkTableIndexes($this->getIndexes()))) {
            $this->handler->fixIndexes($this->getIndexes());
        }
    }
}
