<?php

declare(strict_types=1);

namespace App\Application\LiveComponents\CommentThread;

use App\Application\LiveComponents\LiveComponentState;

/**
 * Type-safe state for CommentThreadComponent
 *
 * Provides compile-time type safety for collaborative comment threads.
 * All properties are readonly and immutable - transformations return new instances.
 */
final readonly class CommentThreadState implements LiveComponentState
{
    public function __construct(
        public array $comments = [],
        public string $sortBy = 'newest',
        public bool $showReactions = true,
        public bool $allowEdit = true,
        public bool $allowDelete = true,
        public int $maxNestingLevel = 3
    ) {
    }

    /**
     * Create from ComponentData (Framework → Domain conversion)
     */
    public static function fromArray(array $data): self
    {
        return new self(
            comments: ($data['comments'] ?? []),
            sortBy: (string) ($data['sort_by'] ?? 'newest'),
            showReactions: (bool) ($data['show_reactions'] ?? true),
            allowEdit: (bool) ($data['allow_edit'] ?? true),
            allowDelete: (bool) ($data['allow_delete'] ?? true),
            maxNestingLevel: (int) ($data['max_nesting_level'] ?? 3)
        );
    }

    /**
     * Create empty state with defaults
     */
    public static function empty(): self
    {
        return new self();
    }

    /**
     * Convert to ComponentData (Domain → Framework conversion)
     */
    public function toArray(): array
    {
        return [
            'comments' => $this->comments,
            'sort_by' => $this->sortBy,
            'show_reactions' => $this->showReactions,
            'allow_edit' => $this->allowEdit,
            'allow_delete' => $this->allowDelete,
            'max_nesting_level' => $this->maxNestingLevel,
        ];
    }

    /**
     * Add new comment
     */
    public function withCommentAdded(
        string $content,
        string $authorId,
        string $authorName,
        ?string $parentId = null
    ): self {
        $comments = $this->comments;

        $newComment = [
            'id' => uniqid('comment_', true),
            'content' => $content,
            'author_id' => $authorId,
            'author_name' => $authorName,
            'parent_id' => $parentId,
            'created_at' => time(),
            'updated_at' => null,
            'reactions' => [],
            'reply_count' => 0,
            'is_edited' => false,
        ];

        $comments[] = $newComment;

        // Update parent's reply count if this is a reply
        if ($parentId) {
            foreach ($comments as &$comment) {
                if ($comment['id'] === $parentId) {
                    $comment['reply_count'] = ($comment['reply_count'] ?? 0) + 1;

                    break;
                }
            }
        }

        return new self(
            comments: $comments,
            sortBy: $this->sortBy,
            showReactions: $this->showReactions,
            allowEdit: $this->allowEdit,
            allowDelete: $this->allowDelete,
            maxNestingLevel: $this->maxNestingLevel
        );
    }

    /**
     * Edit comment
     */
    public function withCommentEdited(string $commentId, string $newContent): self
    {
        $comments = $this->comments;

        foreach ($comments as &$comment) {
            if ($comment['id'] === $commentId) {
                $comment['content'] = $newContent;
                $comment['updated_at'] = time();
                $comment['is_edited'] = true;

                break;
            }
        }

        return new self(
            comments: $comments,
            sortBy: $this->sortBy,
            showReactions: $this->showReactions,
            allowEdit: $this->allowEdit,
            allowDelete: $this->allowDelete,
            maxNestingLevel: $this->maxNestingLevel
        );
    }

    /**
     * Delete comment and its replies
     */
    public function withCommentDeleted(string $commentId): self
    {
        $comments = array_filter($this->comments, function ($comment) use ($commentId) {
            return $comment['id'] !== $commentId && $comment['parent_id'] !== $commentId;
        });

        return new self(
            comments: array_values($comments),
            sortBy: $this->sortBy,
            showReactions: $this->showReactions,
            allowEdit: $this->allowEdit,
            allowDelete: $this->allowDelete,
            maxNestingLevel: $this->maxNestingLevel
        );
    }

    /**
     * Add or toggle reaction
     */
    public function withReactionToggled(string $commentId, string $reaction, string $userId): self
    {
        $comments = $this->comments;

        foreach ($comments as &$comment) {
            if ($comment['id'] === $commentId) {
                $reactions = $comment['reactions'] ?? [];

                // Check if user already reacted with this
                $existingIndex = -1;
                foreach ($reactions as $index => $r) {
                    if ($r['user_id'] === $userId && $r['type'] === $reaction) {
                        $existingIndex = $index;

                        break;
                    }
                }

                if ($existingIndex === -1) {
                    // Add new reaction
                    $reactions[] = [
                        'type' => $reaction,
                        'user_id' => $userId,
                        'created_at' => time(),
                    ];
                } else {
                    // Remove reaction (toggle)
                    array_splice($reactions, $existingIndex, 1);
                }

                $comment['reactions'] = $reactions;

                break;
            }
        }

        return new self(
            comments: $comments,
            sortBy: $this->sortBy,
            showReactions: $this->showReactions,
            allowEdit: $this->allowEdit,
            allowDelete: $this->allowDelete,
            maxNestingLevel: $this->maxNestingLevel
        );
    }

    /**
     * Change sorting
     */
    public function withSorting(string $sortBy): self
    {
        return new self(
            comments: $this->comments,
            sortBy: $sortBy,
            showReactions: $this->showReactions,
            allowEdit: $this->allowEdit,
            allowDelete: $this->allowDelete,
            maxNestingLevel: $this->maxNestingLevel
        );
    }

    /**
     * Sort comments by criteria
     */
    public function getSortedComments(): array
    {
        $sorted = $this->comments;

        usort($sorted, function ($a, $b) {
            return match ($this->sortBy) {
                'newest' => ($b['created_at'] ?? 0) <=> ($a['created_at'] ?? 0),
                'oldest' => ($a['created_at'] ?? 0) <=> ($b['created_at'] ?? 0),
                'most_liked' => count($b['reactions'] ?? []) <=> count($a['reactions'] ?? []),
                default => 0
            };
        });

        return $sorted;
    }

    /**
     * Build comment tree
     */
    public function buildCommentTree(?string $parentId = null, int $level = 0): array
    {
        if ($level >= $this->maxNestingLevel) {
            return [];
        }

        $tree = [];
        $sortedComments = $this->getSortedComments();

        foreach ($sortedComments as $comment) {
            $commentParentId = $comment['parent_id'] ?? null;

            if ($commentParentId === $parentId) {
                $comment['level'] = $level;
                $comment['replies'] = $this->buildCommentTreeForComments(
                    $sortedComments,
                    $comment['id'],
                    $level + 1
                );
                $tree[] = $comment;
            }
        }

        return $tree;
    }

    /**
     * Helper for building tree recursively
     */
    private function buildCommentTreeForComments(array $comments, string $parentId, int $level): array
    {
        if ($level >= $this->maxNestingLevel) {
            return [];
        }

        $tree = [];

        foreach ($comments as $comment) {
            $commentParentId = $comment['parent_id'] ?? null;

            if ($commentParentId === $parentId) {
                $comment['level'] = $level;
                $comment['replies'] = $this->buildCommentTreeForComments($comments, $comment['id'], $level + 1);
                $tree[] = $comment;
            }
        }

        return $tree;
    }

    /**
     * Get statistics
     */
    public function getStatistics(): array
    {
        $totalComments = count($this->comments);
        $topLevelComments = count(array_filter($this->comments, fn ($c) => ! isset($c['parent_id']) || $c['parent_id'] === null));
        $totalReactions = array_sum(array_map(fn ($c) => count($c['reactions'] ?? []), $this->comments));

        return [
            'total_comments' => $totalComments,
            'top_level_count' => $topLevelComments,
            'total_reactions' => $totalReactions,
            'has_comments' => $totalComments > 0,
        ];
    }

    /**
     * Get sort label
     */
    public function getSortLabel(): string
    {
        return match ($this->sortBy) {
            'newest' => 'Neueste zuerst',
            'oldest' => 'Älteste zuerst',
            'most_liked' => 'Meist geliked',
            default => 'Neueste zuerst'
        };
    }

    /**
     * Get sort options with current selection
     */
    public function getSortOptions(): array
    {
        return [
            [
                'value' => 'newest',
                'label' => 'Neueste zuerst',
                'is_selected' => $this->sortBy === 'newest' ? 'selected' : '',
            ],
            [
                'value' => 'oldest',
                'label' => 'Älteste zuerst',
                'is_selected' => $this->sortBy === 'oldest' ? 'selected' : '',
            ],
            [
                'value' => 'most_liked',
                'label' => 'Meist geliked',
                'is_selected' => $this->sortBy === 'most_liked' ? 'selected' : '',
            ],
        ];
    }

}
