<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Shared\Formatters;

use App\Framework\Mcp\Core\ValueObjects\OutputFormat;

/**
 * Table Output Formatter
 *
 * Formatiert Daten als tabellarische Struktur mit Headers und Rows
 */
final readonly class TableFormatter implements OutputFormatter
{
    public function format(mixed $data): array
    {
        $tableData = $this->normalizeToArray($data);

        if (empty($tableData)) {
            return [
                'content' => $this->createEmptyTable(),
                'format' => $this->getFormat()->value,
                'headers' => [],
                'rows' => [],
                'total_rows' => 0,
            ];
        }

        $headers = $this->extractHeaders($tableData);
        $rows = $this->extractRows($tableData, $headers);

        return [
            'content' => $this->createTable($headers, $rows),
            'format' => $this->getFormat()->value,
            'headers' => $headers,
            'rows' => $rows,
            'total_rows' => count($rows),
            'total_columns' => count($headers),
        ];
    }

    public function supports(OutputFormat $format): bool
    {
        return $format === OutputFormat::TABLE;
    }

    public function getFormat(): OutputFormat
    {
        return OutputFormat::TABLE;
    }

    public function getDescription(): string
    {
        return $this->getFormat()->getDescription();
    }

    private function normalizeToArray(mixed $data): array
    {
        if (is_array($data)) {
            return $data;
        }

        if (is_object($data)) {
            return (array) $data;
        }

        return [$data];
    }

    private function extractHeaders(array $data): array
    {
        if (empty($data)) {
            return [];
        }

        $firstRow = reset($data);

        if (is_array($firstRow)) {
            return array_keys($firstRow);
        }

        if (is_object($firstRow)) {
            return array_keys((array) $firstRow);
        }

        return ['value'];
    }

    private function extractRows(array $data, array $headers): array
    {
        $rows = [];

        foreach ($data as $item) {
            if (is_array($item) || is_object($item)) {
                $itemArray = is_array($item) ? $item : (array) $item;
                $row = [];

                foreach ($headers as $header) {
                    $value = $itemArray[$header] ?? '';
                    $row[] = $this->formatValue($value);
                }

                $rows[] = $row;
            } else {
                $rows[] = [$this->formatValue($item)];
            }
        }

        return $rows;
    }

    private function formatValue(mixed $value): string
    {
        if (is_bool($value)) {
            return $value ? 'true' : 'false';
        }

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

        if (is_array($value)) {
            return '[' . implode(', ', array_map([$this, 'formatValue'], $value)) . ']';
        }

        if (is_object($value)) {
            return get_class($value);
        }

        return (string) $value;
    }

    private function createTable(array $headers, array $rows): string
    {
        if (empty($headers) && empty($rows)) {
            return $this->createEmptyTable();
        }

        // Calculate column widths
        $columnWidths = $this->calculateColumnWidths($headers, $rows);

        $table = '';

        // Header row
        if (! empty($headers)) {
            $table .= $this->createRow($headers, $columnWidths);
            $table .= $this->createSeparator($columnWidths);
        }

        // Data rows
        foreach ($rows as $row) {
            $table .= $this->createRow($row, $columnWidths);
        }

        return $table;
    }

    private function createEmptyTable(): string
    {
        return "No data available\n";
    }

    private function calculateColumnWidths(array $headers, array $rows): array
    {
        $widths = [];

        // Initialize with header lengths
        foreach ($headers as $index => $header) {
            $widths[$index] = strlen($header);
        }

        // Check row data
        foreach ($rows as $row) {
            foreach ($row as $index => $cell) {
                $cellLength = strlen($cell);
                $widths[$index] = max($widths[$index] ?? 0, $cellLength);
            }
        }

        // Minimum width and maximum width limits
        return array_map(fn ($width) => max(3, min(50, $width)), $widths);
    }

    private function createRow(array $cells, array $columnWidths): string
    {
        $row = '| ';

        foreach ($cells as $index => $cell) {
            $width = $columnWidths[$index] ?? 10;
            $truncatedCell = strlen($cell) > $width ? substr($cell, 0, $width - 3) . '...' : $cell;
            $row .= str_pad($truncatedCell, $width) . ' | ';
        }

        return $row . "\n";
    }

    private function createSeparator(array $columnWidths): string
    {
        $separator = '|-';

        foreach ($columnWidths as $width) {
            $separator .= str_repeat('-', $width) . '-|-';
        }

        return $separator . "\n";
    }
}
