<?php

declare(strict_types=1);

namespace App\DTOs;

/**
 * Lightweight value object representing a user's recommendation preference profile.
 *
 * Built by UserPreferenceBuilder from signals:
 *   - favourited artworks  (+3)
 *   - awards given         (+5)
 *   - creator follows      (+2 for their tags)
 *   - own uploads          (category bias)
 *
 * Cached in `user_reco_profiles` with a configurable TTL (default 6 hours).
 */
final class UserRecoProfileDTO
{
    /**
     * @param array<int, string>           $topTagSlugs          Top tag slugs by weighted score (up to 20)
     * @param array<int, string>           $topCategorySlugs     Top category slugs (up to 5)
     * @param array<int, int>              $strongCreatorIds     Followed creator user IDs (up to 50)
     * @param array<string, float>         $tagWeights           Tag slug → normalised weight (0–1)
     * @param array<string, float>         $categoryWeights      Category slug → normalised weight
     * @param array<int, string>           $dislikedTagSlugs     Future: blocked/hidden tag slugs
     */
    public function __construct(
        public readonly array $topTagSlugs      = [],
        public readonly array $topCategorySlugs = [],
        public readonly array $strongCreatorIds = [],
        public readonly array $tagWeights       = [],
        public readonly array $categoryWeights  = [],
        public readonly array $dislikedTagSlugs = [],
    ) {}

    /**
     * True if the user has enough signals to drive personalised recommendations.
     */
    public function hasSignals(): bool
    {
        return $this->topTagSlugs !== [] || $this->strongCreatorIds !== [];
    }

    /**
     * Returns the normalised tag weight for a given slug (0.0 if unknown).
     */
    public function tagWeight(string $slug): float
    {
        return (float) ($this->tagWeights[$slug] ?? 0.0);
    }

    /**
     * Returns true when the creator is in the user's strong-follow list.
     */
    public function followsCreator(int $userId): bool
    {
        return in_array($userId, $this->strongCreatorIds, true);
    }

    /**
     * Serialise for storage in the DB / Redis cache.
     *
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'top_tags'         => $this->topTagSlugs,
            'top_categories'   => $this->topCategorySlugs,
            'strong_creators'  => $this->strongCreatorIds,
            'tag_weights'      => $this->tagWeights,
            'category_weights' => $this->categoryWeights,
            'disliked_tags'    => $this->dislikedTagSlugs,
        ];
    }

    /**
     * Re-hydrate from a stored array (e.g. from the DB JSON column).
     *
     * @param array<string, mixed> $data
     */
    public static function fromArray(array $data): self
    {
        return new self(
            topTagSlugs:      (array) ($data['top_tags']         ?? []),
            topCategorySlugs: (array) ($data['top_categories']   ?? []),
            strongCreatorIds: array_map('intval', (array) ($data['strong_creators'] ?? [])),
            tagWeights:       array_map('floatval', (array) ($data['tag_weights']   ?? [])),
            categoryWeights:  array_map('floatval', (array) ($data['category_weights'] ?? [])),
            dislikedTagSlugs: (array) ($data['disliked_tags']    ?? []),
        );
    }
}
