<?php

declare(strict_types=1);

namespace App\Framework\View\Table\ValueObjects;

use App\Framework\View\Table\TableCell;
use App\Framework\View\Table\TableColumn;
use App\Framework\View\Table\TableRow;

final readonly class TableRowData
{
    /** @param mixed[] $values */
    public function __construct(
        public array $values,
        public ?string $cssClass = null,
        public ?string $id = null
    ) {
    }

    /**
     * Create TableRowData from values matching column order
     *
     * @param ColumnDefinition[] $columns
     * @param mixed ...$values
     */
    public static function fromValues(array $columns, mixed ...$values): self
    {
        if (count($values) !== count($columns)) {
            throw new \InvalidArgumentException(
                'Value count must match column count'
            );
        }

        // Create associative array with column keys
        $indexedValues = [];
        foreach ($columns as $index => $column) {
            $indexedValues[$column->key] = $values[$index];
        }

        return new self($indexedValues);
    }

    /**
     * Create TableRowData from associative array
     */
    public static function fromArray(array $data, ?string $cssClass = null, ?string $id = null): self
    {
        return new self($data, $cssClass, $id);
    }

    /**
     * Add CSS class to this row
     */
    public function withCssClass(string $cssClass): self
    {
        return new self($this->values, $cssClass, $this->id);
    }

    /**
     * Add ID to this row
     */
    public function withId(string $id): self
    {
        return new self($this->values, $this->cssClass, $id);
    }

    /**
     * Get value for specific column
     */
    public function getValue(string $columnKey): mixed
    {
        return $this->values[$columnKey] ?? null;
    }

    /**
     * Check if row has value for column
     */
    public function hasValue(string $columnKey): bool
    {
        return array_key_exists($columnKey, $this->values);
    }

    /**
     * Get all values as associative array
     */
    public function toArray(): array
    {
        return $this->values;
    }

    /**
     * Convert to TableRow for use in existing Table class
     *
     * @param TableColumn[] $columns
     */
    public function toTableRow(array $columns): TableRow
    {
        $cells = [];

        foreach ($columns as $column) {
            $value = $this->getValue($column->key);

            // Apply column formatter if available
            if ($column->formatter !== null) {
                $formattedValue = $column->formatter->format($value);
            } else {
                $formattedValue = $this->formatDefaultValue($value);
            }

            $cells[] = new TableCell(
                value: $formattedValue,
                cssClass: $column->cssClass,
                type: $column->defaultType  // Pass the CellType so HTML isn't escaped
            );
        }

        return new TableRow(
            cells: $cells,
            cssClass: $this->cssClass
        );
    }

    /**
     * Default value formatting when no custom formatter is provided
     */
    private function formatDefaultValue(mixed $value): string
    {
        if ($value === null) {
            return '';
        }

        if (is_bool($value)) {
            return $value ? 'Yes' : 'No';
        }

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

        return (string) $value;
    }

    /**
     * Validate this row against column definitions
     *
     * @param ColumnDefinition[] $columns
     */
    public function validateAgainstColumns(array $columns): void
    {
        foreach ($columns as $column) {
            $value = $this->getValue($column->key);
            $column->validateValue($value);
        }
    }
}
