<?php
namespace App\Components;

use Core\Component;
use Core\View;

class DataTable extends Component
{
    protected array $headers = [];
    protected array $rows = [];
    protected string $id = 'datatable';

    protected array $columnRenderers = [];
    protected array $extraColumns = [];
    protected ?string $groupByColumn = null;
    protected array $filters = [];

    protected static bool $assetsLoaded = false;

    public static function make(): static
    {
        return new static();
    }

    public function id(string $id): static
    {
        $this->id = $id;
        return $this;
    }

    public function headers(array $headers): static
    {
        $this->headers = $headers; // key => label
        return $this;
    }

    public function rows(array $rows): static
    {
        $this->rows = $this->ensureRowObjects($rows);
        return $this;
    }

    public function formatColumn(string $key, callable $renderer): static
    {
        $this->columnRenderers[$key] = $renderer;
        return $this;
    }

    public function columnRenderer(string $key, callable $renderer): static
    {
        return $this->formatColumn($key, $renderer);
    }

    public function addColumn(string $header, callable $callback): static
    {
        $this->headers[$header] = $header;
        $this->extraColumns[$header] = $callback;
        return $this;
    }

    /**
     * Set a column to group by.
     *
     * @param string $column The key of the column to group.
     * @return static
     */
    public function groupBy(string $column): static
    {
        $this->groupByColumn = $column;
        return $this;
    }
    
    /**
     * Add a filter input for a specific column.
     *
     * @param string $column The column key to filter.
     * @param string $type The input type ('text', 'select').
     * @param array|null $options Key-value pairs for select dropdown options.
     * @return static
     */
    public function addFilter(string $column, string $type = 'text', ?array $options = null): static
    {
        $this->filters[] = [
            'column' => $column,
            'type' => $type,
            'options' => $options
        ];
        return $this;
    }

    /**
     * Add a date range filter for a specific column.
     *
     * @param string $column The column key to filter.
     * @return static
     */
    public function addDateRangeFilter(string $column): static
    {
        $this->filters[] = [
            'column' => $column,
            'type' => 'date_range'
        ];
        return $this;
    }

    public function render(): string
    {
        if (!self::$assetsLoaded) {
            // CSS
            View::addHead('<link href="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-1.13.8/b-2.4.2/b-html5-2.4.2/b-print-2.4.2/r-2.5.0/rg-1.4.1/datatables.min.css" rel="stylesheet" />');
            View::addHead('<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">');
            
            // JS
            View::addScript('<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>');
            View::addScript('<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script>');
            View::addScript('<script src="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-1.13.8/b-2.4.2/b-html5-2.4.2/b-print-2.4.2/r-2.5.0/rg-1.4.1/datatables.min.js"></script>');
            
            self::$assetsLoaded = true;
        }

        $filtersHtml = $this->renderFilters();
        $tableHeaders = $this->renderHeaders();
        $tableRows = $this->renderRows();

        // Prepare headers & columns
        $headers = array_keys($this->headers);
        $columnsConfig = json_encode(
            array_map(fn($key) => ['name' => $key], $headers)
        );

        // RowGroup + ordering
        $rowGroupConfig = '';
        $orderConfig = '';
        if ($this->groupByColumn) {
            $columnIndex = array_search($this->groupByColumn, $headers, true);
            if ($columnIndex !== false) {
                $rowGroupConfig = "rowGroup: { dataSrc: $columnIndex },";
                $orderConfig = "order: [[ $columnIndex, 'asc' ]],";
            }
        }

        return <<<HTML
        <div class="mt-2">
            <div id="{$this->id}-controls" class="d-flex flex-wrap align-items-center mb-3">
                <div class="d-flex flex-wrap me-auto">
                    {$filtersHtml}
                </div>
            </div>
            <table id="{$this->id}" class="display nowrap stripe hover" style="width:100%">
                <thead>
                    <tr>{$tableHeaders}</tr>
                </thead>
                <tbody>
                    {$tableRows}
                </tbody>
            </table>
        </div>
        <script>
            document.addEventListener("DOMContentLoaded", function () {
                const table = $("#{$this->id}").DataTable({
                    responsive: true,
                    lengthChange: false,
                    buttons: ["copy", "excel", "pdf", "print"],
                    scrollX: true,
                    columns: {$columnsConfig}.map(col => ({
                        ...col,
                        render: function (data, type, row) {
                            if (type === 'filter' || type === 'search') {
                                // Strip HTML tags before filtering/searching
                                return $('<div>').html(data).text();
                            }
                            return data;
                        }
                    })),
                    language: {
                        paginate: {
                            previous: "<i class='ri-arrow-left-s-line'></i>",
                            next: "<i class='ri-arrow-right-s-line'></i>"
                        }
                    },
                    drawCallback: function() {
                        $(".dataTables_paginate > .pagination").addClass("pagination-rounded");
                    },
                    {$orderConfig}
                    {$rowGroupConfig}
                    dom: 'Bfrtip'
                });


                // Move buttons to the filter controls container
                table.buttons().container().appendTo("#{$this->id}-controls");
                
                // Filter logic
                $('#{$this->id}-controls').on('change keyup', '.filter-input', function() {
                    const columnName = $(this).data('column');
                    const columnIndex = table.column(columnName + ':name').index(); // resolve to actual index
                    let val = $.fn.dataTable.util.escapeRegex($(this).val());

                    if ($(this).data('type') === 'date_range') {
                        const minDateStr = $('#{$this->id}-filter-' + columnName + '-min').val();
                        const maxDateStr = $('#{$this->id}-filter-' + columnName + '-max').val();

                        $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
                            const tableDateStr = data[columnIndex];
                            if (!tableDateStr) return false;

                            const tableDateOnlyStr = tableDateStr.split(' ')[0];
                            const isWithinRange =
                                (minDateStr === '' || tableDateOnlyStr >= minDateStr) &&
                                (maxDateStr === '' || tableDateOnlyStr <= maxDateStr);

                            return isWithinRange;
                        });

                        table.draw();
                        $.fn.dataTable.ext.search.pop();
                    } else {
                        table.column(columnIndex).search(val ? val : '', false, false).draw();
                    }
                });

            });
        </script>
        HTML;
    }


    private function renderHeaders(): string
    {
        return implode('', array_map(fn($label, $key) => "<th data-dt-name='{$key}'>{$label}</th>", array_values($this->headers), array_keys($this->headers)));
    }

    private function renderRows(): string
    {
        return implode('', array_map(function($row) {
            $cells = '';
            foreach ($this->headers as $key => $label) {
                $value = $row[$key] ?? '';

                // Automatic date formatting for common date fields
                if (in_array($key, ['created_at', 'updated_at']) && !empty($value)) {
                    if (is_numeric($value) && $value > 0) { // Check for Unix timestamp
                        $value = date('Y-m-d H:i:s', $value);
                    } else {
                        $timestamp = strtotime($value);
                        if ($timestamp !== false) {
                            $value = date('Y-m-d H:i:s', $timestamp);
                        }
                    }
                }

                if (isset($this->columnRenderers[$key])) {
                    $value = call_user_func($this->columnRenderers[$key], $value, $row);
                }

                if (isset($this->extraColumns[$key])) {
                    $value = call_user_func($this->extraColumns[$key], $row);
                }

                $cells .= "<td>{$value}</td>";
            }
            return "<tr>{$cells}</tr>";
        }, $this->rows));
    }
    
    private function renderFilters(): string
    {
        if (empty($this->filters)) {
            return '';
        }

        $html = '<div class="row gx-2 gy-2">';
        foreach ($this->filters as $filter) {
            $label = $this->headers[$filter['column']] ?? $filter['column'];
            $html .= '<div class="col-auto">';
            $html .= '<label for="' . $this->id . '-filter-' . $filter['column'] . '" class="form-label visually-hidden">' . $label . '</label>';

            if ($filter['type'] === 'text') {
                $html .= '<input type="text" class="form-control form-control-sm filter-input" data-column="' . $filter['column'] . '" id="' . $this->id . '-filter-' . $filter['column'] . '" placeholder="Search by ' . $label . '">';
            } elseif ($filter['type'] === 'select' && isset($filter['options'])) {
                $html .= '<select class="form-select form-select-sm filter-input" data-column="' . $filter['column'] . '" id="' . $this->id . '-filter-' . $filter['column'] . '">';
                $html .= '<option value="">All ' . $label . '</option>';
                foreach ($filter['options'] as $value => $optionLabel) {
                    $html .= '<option value="' . $value . '">' . $optionLabel . '</option>';
                }
                $html .= '</select>';
            } elseif ($filter['type'] === 'date_range') {
                $html .= '<div class="input-group input-group-sm">';
                $html .= '<input type="date" class="form-control filter-input" data-type="date_range" data-column="' . $filter['column'] . '" id="' . $this->id . '-filter-' . $filter['column'] . '-min" title="Start Date">';
                $html .= '<span class="input-group-text">-</span>';
                $html .= '<input type="date" class="form-control filter-input" data-type="date_range" data-column="' . $filter['column'] . '" id="' . $this->id . '-filter-' . $filter['column'] . '-max" title="End Date">';
                $html .= '</div>';
            }
            $html .= '</div>';
        }
        $html .= '</div>';
        return $html;
    }

    /**
     * Ensures all rows are associative arrays.
     * @param array $rows
     * @return array
     */
    private function ensureRowObjects(array $rows): array
    {
        return array_map(function($row) {
            if (is_object($row)) {
                $row = (array) $row;
            }

            return $this->flattenRow($row);
        }, $rows);
    }

    private function flattenRow(array $row, string $prefix = ''): array
    {
        $flattened = [];

        foreach ($row as $key => $value) {
            $newKey = $prefix ? $prefix . '|' . $key : $key;

            if (is_array($value)) {
                $flattened += $this->flattenRow($value, $newKey);
            } else {
                $flattened[$newKey] = $value;
            }
        }

        return $flattened;
    }


}
