<?php

namespace Ignite\Core\Models\Grid;

use Illuminate\Support\Carbon;
use Ignite\Core\Facades\Format;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Traits\Macroable;

class Formatter
{
    use Macroable;

    /**
     * Custom the typical html for buttons.
     *
     * @param  array  $actions
     * @return string
     */
    public function buttons(array $actions): string
    {
        $first = array_shift($actions);
        $button = $this->buttonFromArray($first);

        $links = [];
        foreach ($actions as $action) {
            $links[] = sprintf('<li>%s</li>', $this->linkFromArray($action));
        }

        $dropdownButton = sprintf(
            '<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span></button>'
        );
        $dropdown = sprintf('<ul class="dropdown-menu">%s</ul>', implode(PHP_EOL, $links));

        $wrapper = sprintf(
            '<div class="btn-group">%s%s%s</div>',
            $button,
            count($links) ? $dropdownButton : '',
            count($links) ? $dropdown : ''
        );

        return trim(preg_replace('/\s+/', ' ', $wrapper));
    }

    /**
     * Create a button from an array of expected keys.
     *
     * @param  array  $button
     * @return string
     */
    public function buttonFromArray(array $button): string
    {
        if (is_null($button)) {
            return '';
        }

        $button = $this->htmlCreateNulls($button);

        return $this->button(
            $button['label'],
            $button['link'],
            $button['type'],
            $button['text'],
            $button['icon'],
            $button['class'],
            $button['click']
        );
    }

    /**
     * Create a link from an array of expected keys.
     *
     * @param  array  $link
     * @return string
     */
    public function linkFromArray(array $link): string
    {
        $link = $this->htmlCreateNulls($link);

        return $this->link(
            $link['label'],
            $link['link'],
            $link['type'],
            $link['text'],
            $link['icon'],
            $link['class'],
            $link['click']
        );
    }

    /**
     * Create null values for any key without a value.
     *
     * @param array $data
     * @return array
     */
    public function htmlCreateNulls(array $data): array
    {
        $keys = ['label', 'link', 'type', 'text', 'icon', 'class', 'click'];

        foreach ($keys as $key) {
            if (! array_key_exists($key, $data)) {
                $data[$key] = '';
            }
        }

        return $data;
    }

    /**
     * Create a button for the grid.
     *
     * @param  string  $label
     * @param  string  $link
     * @param  string  $type
     * @param  string  $text
     * @param  string  $icon
     * @param  string  $class
     * @param  string  $click
     * @return string
     */
    public function button(string $label, string $link, string $type = 'default', string $text = '', string $icon = '', string $class = '', string $click = ''): string
    {
        $type = empty($type) ? 'default' : $type;
        $text = empty($text) ? null : $text;
        $class = trim($class . ' btn-' . str_slug($label));

        return sprintf(
            '<a href="%s" class="btn btn-sm btn-%s %s"%s><span class="%s">%s%s</span></a>',
            $link ?? '',
            $type ?? '',
            $class ?? '',
            empty($click) ? '' : " data-message='$click'",
            $text ?? '',
            empty($icon) ? '' : sprintf('<i class="fa-solid fa-%s"></i> ', $icon),
            $label ?? ''
        );
    }

    /**
     * Create a link for the grid or dropdown.
     *
     * @param  string  $label
     * @param  string  $link
     * @param  string  $type
     * @param  string  $text
     * @param  string  $icon
     * @param  string  $class
     * @param  string  $click
     * @return string
     */
    public function link(string $label, string $link, string $type = 'default', string $text = '', string $icon = '', string $class = '', string $click = ''): string
    {
        $type = empty($type) ? 'default' : $type;
        $text = empty($text) ? null : $text;
        $class = $class . ' ' . str_slug($label);

        return sprintf(
            '<a href="%s" class="%s %s"%s><span class="%s">%s%s</span></a>',
            $link ?? '',
            $type ?? '',
            $class ?? '',
            empty($click) ? '' : " data-message='$click'",
            $text ?? '',
            empty($icon) ? '' : sprintf('<i class="fa-solid fa-%s"></i> ', $icon),
            $label ?? ''
        );
    }

    /**
     * Format the status column.
     *
     * @param  object  $model
     * @return HtmlString|string
     */
    public function status(object $model): HtmlString|string
    {
        return $this->html(ucwords(strtolower($model->status)));
    }

    /**
     * Format the created at column.
     *
     * @param  object  $model
     * @return HtmlString
     */
    public function createdAt(object $model): HtmlString
    {
        return $this->formatDate($model, 'created_at', 'Y-m-d H:i:s');
    }

    /**
     * Format the updated at column.
     *
     * @param  object  $model
     * @return HtmlString
     */
    public function updatedAt(object $model): HtmlString
    {
        return $this->formatDate($model, 'updated_at', 'Y-m-d H:i:s');
    }

    /**
     * Format the deleted at column.
     *
     * @param  object  $model
     * @return HtmlString
     */
    public function deletedAt(object $model): HtmlString
    {
        return $this->formatDate($model, 'deleted_at', 'Y-m-d H:i:s');
    }

    /**
     * Check if a value is falsy and return No or Yes if truthy.
     *
     * @param  mixed $value
     * @return string
     */
    public function formatYesNo(mixed $value): string
    {
        return ! $value ? trans('Core::core.common.no') : trans('Core::core.common.yes');
    }

    /**
     * Format a number for the grid.
     *
     * @param  object  $model
     * @param  string  $key
     * @param  int  $decimals
     * @return HtmlString
     */
    public function formatNumber(object $model, string $key, int $decimals = 0): HtmlString
    {
        return Format::amount($model->$key, $decimals);
    }

    /**
     * Format a amount for the grid.
     *
     * @param  object  $model
     * @param  string  $key
     * @return HtmlString|string
     */
    public function formatAmount(object $model, string $key)
    {
        if (is_object($model) && property_exists($model, $key)) {
            return Format::amount($model->$key);
        }

        return $model;
    }

    /**
     * Format an amount with a currency.
     *
     * @param  object  $model
     * @param  string  $key
     * @param  string  $currency
     * @param  int  $decimals
     * @return string
     */
    public function formatCurrency(object $model, string $key, string $currency = '$', int $decimals = 2): string
    {
        return Format::currency($model->$key, $currency, $decimals);
    }

    /**
     * Format a number with a percentage.
     *
     * @param  object  $model
     * @param  string  $key
     * @param  int  $decimals
     * @return string
     */
    public function formatPercentage(object $model, string $key, int $decimals = 2): string
    {
        return $this->formatNumber($model, $key, $decimals) . '%';
    }

    /**
     * Format a date for the grid.
     *
     * @param  object  $model
     * @param  string  $key
     * @param  string  $format
     * @param  string  $from
     * @return HtmlString
     */
    public function formatDate(object $model, string $key, string $format = 'Y-m-d', string $from = 'Y-m-d H:i:s'): HtmlString
    {
        if (! isset($model->$key)) {
            return $this->html('');
        }

        $date = $model->$key;

        if (! $date instanceof Carbon) {
            $date = Carbon::createFromFormat($from, $date, config('app.timezone'));
        }

        return $this->html($date->format($format));
    }

    /**
     * Format a checkbox for the grid.
     *
     * @param  int  $id
     * @return HtmlString
     */
    public function formatCheckbox(int $id): HtmlString
    {
        return $this->html(sprintf(
            '<input type="checkbox" class="dt-checkboxes" name="entries[]" value="%s" />',
            $id
        ));
    }

    /**
     * Format the value as a HTML string.
     *
     * @param  string  $value
     * @return HtmlString
     */
    public function html(string $value): HtmlString
    {
        return new HtmlString($value);
    }
}
