<?php

declare(strict_types=1);

namespace App\Application\LiveComponents\ActivityFeed;

use App\Application\LiveComponents\Services\ActivityDataProvider;
use App\Application\LiveComponents\ValueObjects\Activity;
use App\Application\LiveComponents\ValueObjects\ActivityFilter;
use App\Application\LiveComponents\ValueObjects\ActivityType;
use App\Framework\LiveComponents\Attributes\Action;
use App\Framework\LiveComponents\Attributes\LiveComponent;
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
use App\Framework\LiveComponents\DataProviderResolver;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;

/**
 * ActivityFeedComponent - Real-time Activity Feed with Live Updates
 *
 * Demonstrates:
 * - Type-safe state management with ActivityFeedState Value Object
 * - Real-time activity updates
 * - Activity filtering by type (comments, likes, shares, follows, mentions, system)
 * - Mark as read/unread functionality
 * - Pagination with load more
 * - Time-based grouping (today, yesterday, this week, older)
 * - User avatars and relative timestamps
 * - Unread count badge
 * - Compile-time type safety for data access
 *
 * Uses ActivityDataProvider for flexible data sources (Demo, Database, API, etc.)
 */
#[LiveComponent('activity-feed')]
final readonly class ActivityFeedComponent implements LiveComponentContract
{
    public function __construct(
        public ComponentId $id,
        public ActivityFeedState $state,
        private readonly DataProviderResolver $dataProviderResolver
    ) {}

    private function getDataProvider(): ActivityDataProvider
    {
        return $this->dataProviderResolver->resolve(
            ActivityDataProvider::class,
            $this->state->dataSource
        );
    }

    public function getRenderData(): ComponentRenderData
    {
        $dataProvider = $this->getDataProvider();

        // If no activities loaded yet, load from data provider
        $activitiesData = $this->state->activities;
        if (empty($activitiesData)) {
            $filterEnum = ActivityFilter::fromString($this->state->filter);
            $activities = $dataProvider->getActivities($filterEnum);
            $activitiesData = $this->activitiesToArray($activities);
        }

        // Paginate activities
        $paginatedActivities = $this->paginateActivities(
            $activitiesData,
            $this->state->page,
            $this->state->pageSize
        );

        // Prepare activities for display
        $preparedActivities = $this->prepareActivitiesForDisplay(
            $paginatedActivities,
            $this->state->showAvatars,
            $this->state->showTimestamps
        );

        // Group by date if enabled
        $groupedActivities = $this->state->groupByDate
            ? $this->groupActivitiesByDate($preparedActivities)
            : [['date_label' => '', 'activities' => $preparedActivities]];

        // Calculate stats
        $unreadCount = $dataProvider->getUnreadCount();
        $totalCount = count($activitiesData);
        $hasMore = ($this->state->page * $this->state->pageSize) < $totalCount;

        // Prepare filter options
        $filterOptions = $this->prepareFilterOptions(
            $this->state->filter,
            $activitiesData,
            $unreadCount
        );

        return new ComponentRenderData(
            templatePath: 'livecomponent-activity-feed',
            data: [
                // Raw data
                'activities' => $activitiesData,
                'filter' => $this->state->filter,
                'page' => $this->state->page,
                'page_size' => $this->state->pageSize,

                // Display data
                'grouped_activities' => $groupedActivities,
                'has_activities' => ! empty($preparedActivities),
                'is_empty' => empty($preparedActivities),
                'show_avatars' => $this->state->showAvatars,
                'show_timestamps' => $this->state->showTimestamps,

                // Stats
                'unread_count' => $unreadCount,
                'total_count' => $totalCount,
                'showing_count' => count($preparedActivities),
                'has_unread' => $unreadCount > 0,
                'unread_badge' => $this->state->getUnreadBadge($unreadCount),

                // Pagination
                'has_more' => $hasMore,
                'current_page' => $this->state->page,
                'load_more_label' => $this->state->getLoadMoreLabel(),

                // Filters
                'filter_options' => $filterOptions,
                'active_filter_label' => ActivityFilter::fromString($this->state->filter)->getLabel(),

                // UI flags
                'show_group_by_date' => $this->state->groupByDate,
                'show_filters' => ! empty($activitiesData),
                'show_mark_all_read' => $unreadCount > 0,
            ]
        );
    }

    // Action methods - Type-safe parameters

    #[Action]
    public function addActivity(
        string $activityType,
        string $userId,
        string $userName,
        string $content,
        ?string $avatarUrl = null,
        array $metadata = []
    ): ActivityFeedState {
        $dataProvider = $this->getDataProvider();

        // Create Activity Value Object
        $activity = new Activity(
            id: uniqid('activity_', true),
            type: ActivityType::fromString($activityType),
            userId: $userId,
            userName: $userName,
            content: $content,
            timestamp: time(),
            isRead: false,
            avatarUrl: $avatarUrl,
            metadata: $metadata
        );

        // Add via data provider
        $dataProvider->addActivity($activity);

        // Reload activities from provider
        $filter = ActivityFilter::fromString($this->state->filter);
        $activities = $dataProvider->getActivities($filter);

        return $this->state->withActivities($this->activitiesToArray($activities));
    }

    #[Action]
    public function removeActivity(string $activityId): ActivityFeedState
    {
        $dataProvider = $this->getDataProvider();

        // Delete via data provider
        $dataProvider->deleteActivity($activityId);

        // Reload activities
        $filter = ActivityFilter::fromString($this->state->filter);
        $activities = $dataProvider->getActivities($filter);

        return $this->state->withActivities($this->activitiesToArray($activities));
    }

    #[Action]
    public function markAsRead(string $activityId): ActivityFeedState
    {
        $dataProvider = $this->getDataProvider();

        // Mark as read via data provider
        $dataProvider->markAsRead($activityId);

        // Reload activities
        $filter = ActivityFilter::fromString($this->state->filter);
        $activities = $dataProvider->getActivities($filter);

        return $this->state->withActivities($this->activitiesToArray($activities));
    }

    #[Action]
    public function markAllAsRead(): ActivityFeedState
    {
        $dataProvider = $this->getDataProvider();

        // Mark all as read via data provider
        $dataProvider->markAllAsRead();

        // Reload activities
        $filter = ActivityFilter::fromString($this->state->filter);
        $activities = $dataProvider->getActivities($filter);

        return $this->state->withActivities($this->activitiesToArray($activities));
    }

    #[Action]
    public function setFilter(string $filter): ActivityFeedState
    {
        $dataProvider = $this->getDataProvider();

        // Load activities with new filter
        $filterEnum = ActivityFilter::fromString($filter);
        $activities = $dataProvider->getActivities($filterEnum);

        return $this->state->withFilter($filter, $this->activitiesToArray($activities));
    }

    #[Action]
    public function loadMore(): ActivityFeedState
    {
        return $this->state->withLoadMore();
    }

    #[Action]
    public function clearAll(): ActivityFeedState
    {
        return $this->state->withCleared();
    }

    /**
     * Get SSE channel for real-time activity updates
     *
     * Uses activity-scoped channel for context-based activity feeds.
     * Extract context from component ID instance identifier.
     *
     * Examples:
     * - "activity-feed:user-123" → "activity:user-123"
     * - "activity-feed:global" → "activity:global"
     * - "activity-feed:team-456" → "activity:team-456"
     */
    public function getSseChannel(): string
    {
        // Extract context from component instance ID
        // ComponentId format: "activity-feed:user-123" or "activity-feed:global"
        $context = $this->id->instanceId;

        // Activity channel for context-based updates
        return "activity:{$context}";
    }

    // Helper methods

    /**
     * Convert Activity objects to arrays
     */
    private function activitiesToArray(array $activities): array
    {
        return array_map(fn (Activity $a) => $a->toArray(), $activities);
    }

    private function paginateActivities(array $activities, int $page, int $pageSize): array
    {
        $offset = ($page - 1) * $pageSize;

        return array_slice($activities, 0, $offset + $pageSize);
    }

    private function prepareActivitiesForDisplay(
        array $activities,
        bool $showAvatars,
        bool $showTimestamps
    ): array {
        return array_map(function ($activity) use ($showAvatars, $showTimestamps) {
            $type = $activity['type'];
            $activityType = ActivityType::fromString($type);

            return [
                'id' => $activity['id'],
                'type' => $type,
                'user_id' => $activity['user_id'],
                'user_name' => $activity['user_name'],
                'avatar_url' => $activity['avatar_url'] ?? $this->state->getDefaultAvatar($activity['user_name']),
                'content' => $activity['content'],
                'metadata' => $activity['metadata'],
                'timestamp' => $activity['timestamp'],
                'is_read' => $activity['is_read'],

                // Display properties from ActivityType enum
                'show_avatar' => $showAvatars,
                'show_timestamp' => $showTimestamps,
                'icon' => $activityType->getIcon(),
                'icon_class' => $activityType->getIconClass(),
                'type_label' => $activityType->getLabel(),
                'relative_time' => $this->state->formatRelativeTime($activity['timestamp']),
                'absolute_time' => $this->state->formatAbsoluteTime($activity['timestamp']),
                'read_class' => $activity['is_read'] ? 'read' : 'unread',
                'activity_class' => "activity-type-{$type}",
            ];
        }, $activities);
    }

    private function groupActivitiesByDate(array $activities): array
    {
        $groups = [
            'today' => [],
            'yesterday' => [],
            'this_week' => [],
            'older' => [],
        ];

        $now = time();
        $todayStart = strtotime('today', $now);
        $yesterdayStart = strtotime('yesterday', $now);
        $weekStart = strtotime('last monday', $now);

        foreach ($activities as $activity) {
            $timestamp = $activity['timestamp'];

            if ($timestamp >= $todayStart) {
                $groups['today'][] = $activity;
            } elseif ($timestamp >= $yesterdayStart) {
                $groups['yesterday'][] = $activity;
            } elseif ($timestamp >= $weekStart) {
                $groups['this_week'][] = $activity;
            } else {
                $groups['older'][] = $activity;
            }
        }

        $result = [];

        if (! empty($groups['today'])) {
            $result[] = ['date_label' => 'Heute', 'activities' => $groups['today']];
        }
        if (! empty($groups['yesterday'])) {
            $result[] = ['date_label' => 'Gestern', 'activities' => $groups['yesterday']];
        }
        if (! empty($groups['this_week'])) {
            $result[] = ['date_label' => 'Diese Woche', 'activities' => $groups['this_week']];
        }
        if (! empty($groups['older'])) {
            $result[] = ['date_label' => 'Älter', 'activities' => $groups['older']];
        }

        return $result;
    }

    private function prepareFilterOptions(string $currentFilter, array $activities, int $unreadCount): array
    {
        $typeCounts = $this->getActivityTypeCounts($activities);

        $filters = [
            ActivityFilter::ALL,
            ActivityFilter::UNREAD,
            ActivityFilter::COMMENTS,
            ActivityFilter::LIKES,
            ActivityFilter::SHARES,
            ActivityFilter::FOLLOWS,
            ActivityFilter::MENTIONS,
            ActivityFilter::SYSTEM,
        ];

        return array_map(function (ActivityFilter $filter) use ($currentFilter, $typeCounts, $unreadCount, $activities) {
            $value = $filter->value;
            $count = match ($filter) {
                ActivityFilter::ALL => count($activities),
                ActivityFilter::UNREAD => $unreadCount,
                default => $typeCounts[$value] ?? 0
            };

            return [
                'value' => $value,
                'label' => $filter->getLabel(),
                'count' => $count,
                'is_active' => $currentFilter === $value,
                'selected' => $currentFilter === $value ? 'selected' : '',
            ];
        }, $filters);
    }

    private function getActivityTypeCounts(array $activities): array
    {
        $counts = [];

        foreach ($activities as $activity) {
            $type = $activity['type'];
            $counts[$type] = ($counts[$type] ?? 0) + 1;
        }

        return $counts;
    }
}
