<?php

namespace Webkul\Support\Filament\Forms\Components;

use Closure;
use Filament\Actions\Action;
use Filament\Forms\Components\Repeater as BaseRepeater;
use Filament\Support\Enums\Size;
use Filament\Tables\Table\Concerns\HasColumnManager;
use Illuminate\Support\Arr;
use Webkul\Support\Filament\Forms\Components\Repeater\TableColumn;

class Repeater extends BaseRepeater
{
    use HasColumnManager;

    protected ?string $columnManagerSessionKey = null;

    /**
     * @var array<Action | Closure>
     */
    protected array $footerActions = [];

    /**
     * @var array<Action> | null
     */
    protected ?array $cachedFooterActions = null;

    protected bool|Closure|null $isRepeaterHasTableView = false;

    public function getDefaultView(): string
    {
        if ($this->hasTableView()) {
            return 'support::filament.forms.components.repeater.table';
        }

        return (string) parent::getDefaultView();
    }

    public function isReorderableWithDragAndDrop(): bool
    {
        return $this->evaluate($this->isReorderableWithDragAndDrop) && $this->isReorderable();
    }

    public function table(array|Closure|null $columns): static
    {
        $this->isRepeaterHasTableView = true;

        $this->tableColumns = $columns;

        return $this;
    }

    public function hasTableView(): bool
    {
        return $this->evaluate($this->isRepeaterHasTableView) || filled($this->getTableColumns());
    }

    /**
     * @param  array<Action | Closure>  $actions
     */
    public function footerActions(array $actions): static
    {
        $this->footerActions = [
            ...$this->footerActions,
            ...$actions,
        ];

        $this->registerActions($actions);

        return $this;
    }

    /**
     * @return array<Action>
     */
    public function getFooterActions(): array
    {
        return $this->cachedFooterActions ??= $this->cacheFooterActions();
    }

    /**
     * @return array<Action>
     */
    protected function cacheFooterActions(): array
    {
        $this->cachedFooterActions = [];

        foreach ($this->footerActions as $footerAction) {
            foreach (Arr::wrap($this->evaluate($footerAction)) as $action) {
                $this->cachedFooterActions[$action->getName()] = $this->getAction($action->getName())
                    ?? $this->prepareAction(
                        $action->defaultColor('gray')->defaultSize(Size::Small),
                    );
            }
        }

        return $this->cachedFooterActions;
    }

    public function getColumnManagerSessionKey(): string
    {
        return $this->columnManagerSessionKey ??= 'repeater_'.$this->getStatePath().'_column_manager';
    }

    public function getMappedColumns(): array
    {
        $columns = $this->evaluate($this->tableColumns);

        if (! is_array($columns)) {
            $columns = [];
        }

        $savedState = session($this->getColumnManagerSessionKey(), []);

        return collect($columns)->map(
            function (TableColumn $column) use ($savedState): array {
                $columnName = $column->getName();

                $isToggled = data_get($savedState, "{$columnName}.isToggled", ! $column->isToggledHiddenByDefault());

                return [
                    'type'                     => 'column',
                    'name'                     => $columnName,
                    'label'                    => $column->getLabel(),
                    'isHidden'                 => $column->isHidden(),
                    'isToggled'                => $isToggled,
                    'isToggleable'             => $column->isToggleable(),
                    'isToggledHiddenByDefault' => $column->isToggledHiddenByDefault(),
                    'isResizable'              => $column->isResizable(),
                    'minWidth'                 => $column->getMinWidth(),
                    'maxWidth'                 => $column->getMaxWidth(),
                ];
            }
        )->toArray();
    }

    public function getTableColumns(): array
    {
        $columns = $this->evaluate($this->tableColumns);

        if (! is_array($columns)) {
            $columns = [];
        }

        $savedState = session($this->getColumnManagerSessionKey(), []);

        $visibleColumns = collect($columns)->filter(
            function (TableColumn $column) use ($savedState): bool {
                if ($column->isHidden()) {
                    return false;
                }

                $columnName = $column->getName();

                if (data_get($savedState, $columnName)) {
                    return data_get($savedState, "{$columnName}.isToggled", false);
                }

                return ! $column->isToggledHiddenByDefault();
            }
        );

        return $visibleColumns->values()->toArray();
    }

    public function hasToggleableColumns(): bool
    {
        $columns = $this->evaluate($this->tableColumns) ?? [];

        return collect($columns)->contains(fn ($column) => $column->isToggleable());
    }

    public function getColumnManagerApplyAction(): Action
    {
        $action = Action::make('applyTableColumnManager')
            ->label(__('filament-tables::table.column_manager.actions.apply.label'))
            ->button()
            ->visible($this->hasDeferredColumnManager())
            ->alpineClickHandler('applyTableColumnManager')
            ->authorize(true);

        if ($this->modifyColumnManagerApplyActionUsing) {
            $action = $this->evaluate($this->modifyColumnManagerApplyActionUsing, [
                'action' => $action,
            ]) ?? $action;
        }

        return $action;
    }

    public function getColumnManagerTriggerAction(): Action
    {
        $action = Action::make('openColumnManager')
            ->label(__('filament-tables::table.actions.column_manager.label'))
            ->iconButton()
            ->icon('heroicon-s-view-columns')
            ->color('gray')
            ->livewireClickHandlerEnabled(false)
            ->authorize(true);

        if ($this->modifyColumnManagerTriggerActionUsing) {
            $action = $this->evaluate($this->modifyColumnManagerTriggerActionUsing, [
                'action' => $action,
            ]) ?? $action;
        }

        if ($action->getView() === Action::BUTTON_VIEW) {
            $action->defaultSize(Size::Small->value);
        }

        return $action;
    }

    public function applyTableColumnManager(?array $columns = null): void
    {
        if (blank($columns)) {
            return;
        }

        $columnState = collect($columns)
            ->filter(fn ($column) => filled(data_get($column, 'name')) && ! is_null(data_get($column, 'isToggled')))
            ->mapWithKeys(fn ($column) => [
                data_get($column, 'name') => [
                    'isToggled'    => data_get($column, 'isToggled'),
                    'isToggleable' => data_get($column, 'isToggleable', true),
                ],
            ])
            ->toArray();

        session([$this->getColumnManagerSessionKey() => $columnState]);
    }

    public function resetTableColumnManager(): void
    {
        session()->forget($this->getColumnManagerSessionKey());
    }

    public function hasDeferredColumnManager(): bool
    {
        return true;
    }

    public function getSummaryForColumn(string $columnName): ?string
    {
        $column = collect($this->getTableColumns())
            ->first(fn (TableColumn $col) => $col->getName() === $columnName);

        if (
            ! $column
            || ! $column->hasSummarizer()
        ) {
            return null;
        }

        $summarizer = $column->getSummarizer();

        $items = collect($this->getState() ?? []);

        if ($items->isEmpty()) {
            return null;
        }

        $value = $summarizer->summarize($items, $columnName);

        if (is_null($value)) {
            return null;
        }

        if ($summarizer->isNumeric() && is_numeric($value)) {
            $value = number_format($value, 2);
        }

        $label = $summarizer->getLabel();

        return $label ? "{$label}: {$value}" : (string) $value;
    }

    public function hasAnySummarizers(): bool
    {
        return collect($this->getTableColumns())
            ->some(fn (TableColumn $column) => $column->hasSummarizer());
    }
}
