<?php

declare(strict_types=1);

namespace App\Framework\View\Table;

use App\Framework\Template\Processing\DomProcessor;
use App\Framework\View\DomWrapper;
use App\Framework\View\RenderContext;

final readonly class TableProcessor implements DomProcessor
{
    public function __construct()
    {
        file_put_contents('/tmp/debug.log', "🔧 TableProcessor: CONSTRUCTOR CALLED - TableProcessor initialized\n", FILE_APPEND | LOCK_EX);
    }

    public function process(DomWrapper $dom, RenderContext $context): DomWrapper
    {
        file_put_contents('/tmp/debug.log', "🔧 TableProcessor: Starting table processing\n", FILE_APPEND | LOCK_EX);

        // Find all <table-data> elements
        $tableDataNodes = $dom->document->querySelectorAll('table-data[source]');
        file_put_contents('/tmp/debug.log', "🔧 TableProcessor: Found " . count($tableDataNodes) . " table-data elements\n", FILE_APPEND | LOCK_EX);

        foreach ($tableDataNodes as $node) {
            $source = $node->getAttribute('source');
            file_put_contents('/tmp/debug.log', "🔧 TableProcessor: Processing table-data with source: '{$source}'\n", FILE_APPEND | LOCK_EX);

            // Get data from context
            $data = $this->resolveDataSource($context->data, $source);
            if ($data === null) {
                file_put_contents('/tmp/debug.log', "🔧 TableProcessor: No data found for source '{$source}'\n", FILE_APPEND | LOCK_EX);

                continue;
            }

            // Get table configuration from attributes
            $tableConfig = $this->parseTableConfig($node);
            file_put_contents('/tmp/debug.log', "🔧 TableProcessor: Table config: " . json_encode($tableConfig) . "\n", FILE_APPEND | LOCK_EX);

            // Generate table based on data type
            $table = $this->createTable($data, $tableConfig);

            // Check if we should wrap the table in a container
            $wrapInContainer = $node->getAttribute('no-container') !== 'true';
            $containerClass = $node->getAttribute('container-class') ?: 'table-container';
            $containerStyle = $node->getAttribute('container-style') ?: '';

            // Replace the table-data element with rendered table
            $tableHtml = $table->render();

            // Wrap table in container div if needed
            if ($wrapInContainer) {
                $styleAttr = $containerStyle ? " style=\"{$containerStyle}\"" : '';
                $tableHtml = "<div class=\"{$containerClass}\"{$styleAttr}>{$tableHtml}</div>";
            }

            file_put_contents('/tmp/debug.log', "🔧 TableProcessor: Generated table HTML (length: " . strlen($tableHtml) . ")\n", FILE_APPEND | LOCK_EX);

            // Create document fragment and replace
            $replacement = $dom->document->createDocumentFragment();
            @$replacement->appendXML($tableHtml);
            $node->parentNode?->replaceChild($replacement, $node);

            file_put_contents('/tmp/debug.log', "🔧 TableProcessor: Successfully replaced table-data element\n", FILE_APPEND | LOCK_EX);
        }

        return $dom;
    }

    private function resolveDataSource(array $contextData, string $source): mixed
    {
        // Handle nested properties like "stats.memory"
        $keys = explode('.', $source);
        $value = $contextData;

        foreach ($keys as $key) {
            if (is_array($value) && array_key_exists($key, $value)) {
                $value = $value[$key];
            } elseif (is_object($value)) {
                if (isset($value->$key)) {
                    $value = $value->$key;
                } elseif (method_exists($value, $key)) {
                    $value = $value->$key();
                } elseif (method_exists($value, 'get' . ucfirst($key))) {
                    $getterMethod = 'get' . ucfirst($key);
                    $value = $value->$getterMethod();
                } else {
                    return null;
                }
            } else {
                return null;
            }
        }

        return $value;
    }

    private function parseTableConfig($node): array
    {
        return [
            'class' => $node->getAttribute('class') ?: 'table',
            'id' => $node->getAttribute('id') ?: null,
            'striped' => $node->getAttribute('striped') === 'true',
            'bordered' => $node->getAttribute('bordered') === 'true',
            'hover' => $node->getAttribute('hover') === 'true',
            'responsive' => $node->getAttribute('responsive') !== 'false',
            'empty-message' => $node->getAttribute('empty-message') ?: 'No data available',
            'headers' => $node->getAttribute('headers'),
            'fields' => $node->getAttribute('fields'),
            'type' => $node->getAttribute('type') ?: 'auto',
        ];
    }

    private function createTable(mixed $data, array $config): Table
    {
        // If data is already a Table object, use it directly
        if ($data instanceof Table) {
            return $data;
        }

        // Fallback to basic table creation for backward compatibility
        return match ($config['type']) {
            'environment' => $this->createEnvironmentTable($data, $config),
            'phpinfo' => $this->createPhpInfoTable($data, $config),
            'routes' => $this->createRouteTable($data, $config),
            'simple' => $this->createSimpleTable($data, $config),
            'auto' => $this->createAutoTable($data, $config),
            default => $this->createAutoTable($data, $config)
        };
    }

    private function createEnvironmentTable(array $data, array $config): Table
    {
        return Table::forEnvironmentVars($data);
    }

    private function createPhpInfoTable(array $data, array $config): Table
    {
        $columns = [
            TableColumn::text('key', 'Setting', 'phpinfo-key'),
            TableColumn::text('value', 'Value', 'phpinfo-value'),
        ];

        $rows = [];
        foreach ($data as $item) {
            $rows[] = TableRow::fromData($item, $columns, 'phpinfo-row');
        }

        $options = new TableOptions(
            striped: $config['striped'],
            bordered: $config['bordered'],
            hover: $config['hover'],
            responsive: $config['responsive'],
            emptyMessage: $config['empty-message']
        );

        return new Table(
            columns: $columns,
            rows: $rows,
            cssClass: 'admin-table phpinfo-table',
            options: $options,
            id: $config['id'] ?: 'phpInfoTable'
        );
    }

    private function createRouteTable(array $data, array $config): Table
    {
        $columns = [
            new TableColumn(
                key: 'method',
                header: 'Method',
                cssClass: 'route-method',
                formatter: $this->createMethodFormatter()
            ),
            TableColumn::text('path', 'Path', 'route-path'),
            new TableColumn(
                key: 'name',
                header: 'Name',
                cssClass: 'route-name',
                formatter: $this->createNameFormatter()
            ),
            TableColumn::text('controller', 'Controller', 'route-controller'),
            TableColumn::text('handler', 'Handler', 'route-handler'),
            new TableColumn(
                key: 'is_protected',
                header: 'Auth',
                cssClass: 'route-auth',
                formatter: $this->createAuthFormatter()
            ),
        ];

        $rows = [];
        foreach ($data as $route) {
            $rows[] = TableRow::fromData($route, $columns, 'route-row');
        }

        $options = new TableOptions(
            striped: $config['striped'],
            bordered: $config['bordered'],
            hover: $config['hover'],
            responsive: $config['responsive'],
            emptyMessage: $config['empty-message']
        );

        return new Table(
            columns: $columns,
            rows: $rows,
            cssClass: 'admin-table route-table',
            options: $options,
            id: $config['id'] ?: 'routesTable'
        );
    }

    private function createSimpleTable(array $data, array $config): Table
    {
        $headers = $config['headers'] ? explode(',', $config['headers']) : [];
        $options = new TableOptions(
            striped: $config['striped'],
            bordered: $config['bordered'],
            hover: $config['hover'],
            responsive: $config['responsive'],
            emptyMessage: $config['empty-message']
        );

        return Table::fromSimpleArray($headers, $data, $options);
    }

    private function createAutoTable(array $data, array $config): Table
    {
        if (empty($data)) {
            return new Table([], [], $config['class']);
        }

        // Auto-detect structure
        $firstItem = reset($data);

        if (is_array($firstItem)) {
            // Array of arrays - use keys as column definitions
            return $this->createTableFromArrayData($data, $config);
        }

        if (is_object($firstItem)) {
            // Array of objects - use properties as columns
            return $this->createTableFromObjectData($data, $config);
        }

        // Simple values - create single column table
        return $this->createSingleColumnTable($data, $config);
    }

    private function createTableFromArrayData(array $data, array $config): Table
    {
        $firstItem = reset($data);
        $columnDefs = [];

        // Create column definitions from first item keys
        foreach (array_keys($firstItem) as $key) {
            $columnDefs[$key] = ucfirst(str_replace('_', ' ', $key));
        }

        $options = new TableOptions(
            striped: $config['striped'],
            bordered: $config['bordered'],
            hover: $config['hover'],
            responsive: $config['responsive'],
            emptyMessage: $config['empty-message']
        );

        return Table::fromArray($data, $columnDefs, $options);
    }

    private function createTableFromObjectData(array $data, array $config): Table
    {
        // Convert objects to arrays for processing
        $arrayData = array_map(function ($item) {
            if (is_object($item)) {
                return get_object_vars($item);
            }

            return $item;
        }, $data);

        return $this->createTableFromArrayData($arrayData, $config);
    }

    private function createSingleColumnTable(array $data, array $config): Table
    {
        $headers = ['Value'];
        $tableData = array_map(fn ($value) => [$value], $data);

        $options = new TableOptions(
            striped: $config['striped'],
            bordered: $config['bordered'],
            hover: $config['hover'],
            responsive: $config['responsive'],
            emptyMessage: $config['empty-message']
        );

        return Table::fromSimpleArray($headers, $tableData, $options);
    }

    private function createMethodFormatter(): \App\Framework\View\Table\CellFormatter
    {
        return new class () implements \App\Framework\View\Table\CellFormatter {
            public function format(mixed $value): string
            {
                if ($value === null || $value === '') {
                    return '';
                }

                $method = strtoupper((string) $value);
                $colorClass = match ($method) {
                    'GET' => 'success',
                    'POST' => 'primary',
                    'PUT', 'PATCH' => 'warning',
                    'DELETE' => 'error',
                    default => 'secondary'
                };

                return sprintf(
                    '<span class="method-badge method-badge--%s">%s</span>',
                    $colorClass,
                    htmlspecialchars($method, ENT_QUOTES)
                );
            }
        };
    }

    private function createNameFormatter(): \App\Framework\View\Table\CellFormatter
    {
        return new class () implements \App\Framework\View\Table\CellFormatter {
            public function format(mixed $value): string
            {
                if ($value === null || $value === '') {
                    return '<em class="text-muted">unnamed</em>';
                }

                // Handle backed enums by accessing their value property
                if ($value instanceof \BackedEnum) {
                    return '<code>' . htmlspecialchars((string) $value->value, ENT_QUOTES) . '</code>';
                }

                return '<code>' . htmlspecialchars((string) $value, ENT_QUOTES) . '</code>';
            }
        };
    }

    private function createAuthFormatter(): \App\Framework\View\Table\CellFormatter
    {
        return new class () implements \App\Framework\View\Table\CellFormatter {
            public function format(mixed $value): string
            {
                $isProtected = (bool) $value;

                if ($isProtected) {
                    return '<span class="auth-badge auth-badge--protected" title="Protected">🔒</span>';
                } else {
                    return '<span class="auth-badge auth-badge--public" title="Public">🌐</span>';
                }
            }
        };
    }
}
