<?php

declare(strict_types=1);

namespace App\Framework\View\Table\Generators;

use App\Framework\View\Table\Table;
use App\Framework\View\Table\TableColumn;
use App\Framework\View\Table\TableGenerator;
use App\Framework\View\Table\TableOptions;
use App\Framework\View\Table\TableRow;

final readonly class GenericArrayTableGenerator implements TableGenerator
{
    public function generate(object|array $data, ?TableOptions $options = null): Table
    {
        if (is_object($data)) {
            return $this->generateFromObject($data, $options);
        }

        if (empty($data)) {
            return new Table([], [], null, $options);
        }

        // Check if it's a simple associative array (key => value)
        if ($this->isSimpleAssociativeArray($data)) {
            return $this->fromKeyValuePairs($data, $options);
        }

        // Check if it's an array of objects/arrays with consistent structure
        $firstItem = reset($data);
        if (is_array($firstItem)) {
            return $this->fromArrayOfArrays($data, $options);
        }

        if (is_object($firstItem)) {
            return $this->fromArrayOfObjects($data, $options);
        }

        // Fallback: treat as simple values
        return $this->fromSimpleValues($data, $options);
    }

    public function supports(object|array $data): bool
    {
        // Generic generator supports everything as fallback
        return true;
    }

    private function generateFromObject(object $data, ?TableOptions $options = null): Table
    {
        $properties = get_object_vars($data);

        return $this->fromKeyValuePairs($properties, $options);
    }

    private function isSimpleAssociativeArray(array $data): bool
    {
        if (empty($data)) {
            return false;
        }

        // Check if all values are scalar (not arrays or objects)
        foreach ($data as $value) {
            if (is_array($value) || is_object($value)) {
                return false;
            }
        }

        // Check if it's not a simple indexed array
        return array_keys($data) !== range(0, count($data) - 1);
    }

    private function fromKeyValuePairs(array $data, ?TableOptions $options = null): Table
    {
        $columns = [
            TableColumn::text('key', 'Key'),
            TableColumn::text('value', 'Value'),
        ];

        $rows = [];
        foreach ($data as $key => $value) {
            $rows[] = TableRow::fromData([
                'key' => $key,
                'value' => $this->formatValue($value),
            ], $columns);
        }

        return new Table($columns, $rows, null, $options);
    }

    private function fromArrayOfArrays(array $data, ?TableOptions $options = null): Table
    {
        $firstItem = reset($data);
        $columns = [];

        // Auto-generate columns from first item keys
        foreach (array_keys($firstItem) as $key) {
            $columns[] = TableColumn::text($key, $this->humanizeKey($key));
        }

        return $this->createTableFromColumns($data, $columns, $options);
    }

    private function fromArrayOfObjects(array $data, ?TableOptions $options = null): Table
    {
        // Convert objects to arrays first
        $arrayData = array_map(function ($item) {
            if (is_object($item)) {
                return get_object_vars($item);
            }

            return $item;
        }, $data);

        return $this->fromArrayOfArrays($arrayData, $options);
    }

    private function fromSimpleValues(array $data, ?TableOptions $options = null): Table
    {
        $columns = [TableColumn::text('value', 'Value')];
        $rows = [];

        foreach ($data as $value) {
            $rows[] = TableRow::fromData(['value' => $this->formatValue($value)], $columns);
        }

        return new Table($columns, $rows, null, $options);
    }

    private function createTableFromColumns(array $data, array $columns, ?TableOptions $options = null): Table
    {
        $rows = [];
        foreach ($data as $rowData) {
            $rows[] = TableRow::fromData($rowData, $columns);
        }

        return new Table($columns, $rows, null, $options);
    }

    private function humanizeKey(string $key): string
    {
        // Convert snake_case to words
        $humanized = str_replace('_', ' ', $key);

        // Convert camelCase to words
        $humanized = preg_replace('/([a-z])([A-Z])/', '$1 $2', $humanized);

        // Capitalize first letter of each word
        return ucwords(strtolower($humanized));
    }

    private function formatValue(mixed $value): string
    {
        if ($value === null) {
            return '';
        }

        if (is_bool($value)) {
            return $value ? 'true' : 'false';
        }

        if (is_array($value) || is_object($value)) {
            return json_encode($value) ?: '[serialization failed]';
        }

        return (string) $value;
    }
}
