<?php

namespace App\Services\Catalog;

class CatalogProductJsonFormatter
{
    public const FORMAT_VERSION = 1;

    /**
     * @return list<array<string, mixed>>
     */
    public function exampleProducts(): array
    {
        return [
            $this->exampleRow(
                retailer: 'kohls',
                name: 'Men\'s FLX Luxury Soft Wander Tee',
                url: 'https://www.kohls.com/product/prd-7768346/mens-flx-luxury-soft-wander-tee.jsp',
                price: '$15.29',
                listPrice: '$21.99',
                rating: '4.8',
                reviewCount: '764',
                imageUrl: 'https://media.kohlsimg.com/is/image/kohls/7768346_Black?wid=342&hei=342&op_sharpen=1',
                keywordPrimary: 'mens flx tee',
                keywordRelated: ['soft wander tee', 'luxury mens tee'],
            ),
            $this->exampleRow(
                retailer: 'kohls',
                name: 'adidas VL Court 3.0 Women\'s Shoes',
                url: 'https://www.kohls.com/product/prd-6554861/adidas-vl-court-30-womens-shoes.jsp',
                price: '$64.99',
                listPrice: '$75.00',
                rating: '4.6',
                reviewCount: '3979',
                imageUrl: 'https://media.kohlsimg.com/is/image/kohls/6554861_Brown?wid=342&hei=342&op_sharpen=1',
                keywordPrimary: 'adidas vl court shoes',
                keywordRelated: ['womens adidas sneakers', 'white court shoes'],
            ),
            $this->exampleRow(
                retailer: 'marshalls',
                name: 'Classic Biker Style Jacket With Smooth Faux Leather Finish',
                url: 'https://www.marshalls.com/us/store/jump/product/women-women-clothing/new-arrivals/Classic-Biker-Style-Jacket-With-Smooth-Faux-Leather-Finish/4000512068?colorId=NS4155690',
                price: '$129.99',
                listPrice: '$195.00',
                rating: '4.7',
                reviewCount: '42',
                imageUrl: 'https://img.marshalls.com/marshalls?set=DisplayName[f2],prd[4000512068_NS4155690],ag[no]&call=url[file:tjxrPRD2_RB.chain]',
                keywordPrimary: 'womens faux leather jacket',
                keywordRelated: ['moto jacket women', 'biker jacket'],
            ),
            $this->exampleRow(
                retailer: 'marshalls',
                name: 'Superlow Loose Jeans',
                url: 'https://www.marshalls.com/us/store/jump/product/women-women-clothing/new-arrivals/Superlow-Loose-Jeans/4000510735?colorId=NS19643052',
                price: '$29.99',
                listPrice: '$57.00',
                rating: '4.5',
                reviewCount: '18',
                imageUrl: 'https://img.marshalls.com/marshalls?set=DisplayName[f2],prd[4000510735_NS19643052],ag[no]&call=url[file:tjxrPRD2_RB.chain]',
                keywordPrimary: 'superlow loose jeans',
                keywordRelated: ['womens baggy jeans', 'low rise jeans'],
            ),
            $this->exampleRow(
                retailer: 'officedepot',
                name: 'Realspace Metal Folding Chair, Black',
                url: 'https://www.officedepot.com/a/products/254901/Realspace-Metal-Folding-Chair-Black/',
                price: '$39.99',
                listPrice: '$49.99',
                rating: '4.0',
                reviewCount: '1205',
                imageUrl: 'https://media.officedepot.com/images/t_large,f_auto/products/254901/1.jpg',
                keywordPrimary: 'metal folding chair',
                keywordRelated: ['black folding chair', 'office folding chair'],
            ),
        ];
    }

    /**
     * @param  list<array<string, mixed>>  $products
     */
    public function encode(array $products, bool $includeExampleMeta = true): string
    {
        $payload = [
            'format_version' => self::FORMAT_VERSION,
            'products' => array_map(function (array $product): array {
                $flat = $this->flattenRow($this->stripInternalFields($product));
                $formatted = $this->formatRow($flat);

                if (filled($product['retailer'] ?? null)) {
                    $formatted['retailer'] = (string) $product['retailer'];
                }

                return $formatted;
            }, $products),
        ];

        if ($includeExampleMeta) {
            $payload['retailer_examples'] = [
                'kohls' => 'Products 1-2',
                'marshalls' => 'Products 3-4',
                'officedepot' => 'Product 5',
            ];
            $payload['field_groups'] = [
                'pricing' => ['price (required)', 'list_price (optional)'],
                'ratings' => ['rating (optional)', 'review_count (optional)'],
                'keywords' => ['primary (required)', 'related (optional array)'],
                'media' => ['image_url (required)'],
            ];
        }

        return json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
    }

    /**
     * @param  array<string, mixed>  $row
     * @return array<string, mixed>
     */
    public function flattenRow(array $row): array
    {
        $pricing = is_array($row['pricing'] ?? null) ? $row['pricing'] : [];
        $ratings = is_array($row['ratings'] ?? null) ? $row['ratings'] : [];
        $keywords = is_array($row['keywords'] ?? null) ? $row['keywords'] : [];
        $media = is_array($row['media'] ?? null) ? $row['media'] : [];
        $productKeywords = is_array($row['product_keywords'] ?? null) ? $row['product_keywords'] : [];

        return [
            'name' => $row['name'] ?? null,
            'url' => $row['url'] ?? null,
            'price' => $pricing['price'] ?? $row['price'] ?? null,
            'list_price' => $pricing['list_price'] ?? $row['list_price'] ?? null,
            'rating' => $ratings['rating'] ?? $row['rating'] ?? null,
            'review_count' => $ratings['review_count'] ?? $row['review_count'] ?? null,
            'keyword_primary' => $keywords['primary']
                ?? $productKeywords['primary']
                ?? $row['keyword_primary']
                ?? null,
            'keyword_related' => $keywords['related']
                ?? $productKeywords['related']
                ?? $row['keyword_related']
                ?? [],
            'image_url' => $media['image_url'] ?? $row['image_url'] ?? null,
            'popularity_note' => $row['popularity_note'] ?? null,
            'matched_keyword' => $row['matched_keyword'] ?? null,
            'keyword_search_volume' => $row['keyword_search_volume'] ?? null,
            'keyword_competition' => $row['keyword_competition'] ?? null,
            'keyword_match_source' => $row['keyword_match_source'] ?? null,
            'keyword_match_reason' => $row['keyword_match_reason'] ?? null,
            'keyword_seeds_generated' => is_array($row['keyword_seeds_generated'] ?? null)
                ? $row['keyword_seeds_generated']
                : null,
        ];
    }

    /**
     * @param  array<string, mixed>  $row
     * @return array<string, mixed>
     */
    public function formatRow(array $row): array
    {
        $formatted = [
            'name' => trim((string) ($row['name'] ?? '')),
            'url' => trim((string) ($row['url'] ?? '')),
            'pricing' => [
                'price' => trim((string) ($row['price'] ?? '')),
            ],
            'media' => [
                'image_url' => trim((string) ($row['image_url'] ?? '')),
            ],
        ];

        if (filled($row['list_price'] ?? null)) {
            $formatted['pricing']['list_price'] = trim((string) $row['list_price']);
        }

        $rating = filled($row['rating'] ?? null) ? trim((string) $row['rating']) : null;
        $reviewCount = filled($row['review_count'] ?? null) ? trim((string) $row['review_count']) : null;

        if ($rating !== null || $reviewCount !== null) {
            $formatted['ratings'] = array_filter([
                'rating' => $rating,
                'review_count' => $reviewCount,
            ], fn (?string $value): bool => $value !== null);
        }

        $keywordPrimary = filled($row['keyword_primary'] ?? null) ? trim((string) $row['keyword_primary']) : null;
        $keywordRelated = is_array($row['keyword_related'] ?? null)
            ? array_values(array_filter($row['keyword_related'], fn (mixed $value): bool => filled($value)))
            : [];

        if ($keywordPrimary !== null || $keywordRelated !== []) {
            $formatted['keywords'] = array_filter([
                'primary' => $keywordPrimary,
                'related' => $keywordRelated !== [] ? $keywordRelated : null,
            ], fn (mixed $value): bool => $value !== null);
        }

        $generatedSeeds = is_array($row['keyword_seeds_generated'] ?? null)
            ? $row['keyword_seeds_generated']
            : null;

        if (filled($row['matched_keyword'] ?? null) || $this->hasGeneratedSeeds($generatedSeeds)) {
            $formatted['keyword_research'] = array_filter([
                'generated_seeds' => $this->formatGeneratedSeeds($generatedSeeds),
                'matched_keyword' => filled($row['matched_keyword'] ?? null)
                    ? trim((string) $row['matched_keyword'])
                    : null,
                'search_volume' => filled($row['keyword_search_volume'] ?? null)
                    ? (int) $row['keyword_search_volume']
                    : null,
                'competition' => filled($row['keyword_competition'] ?? null)
                    ? trim((string) $row['keyword_competition'])
                    : null,
                'match_source' => filled($row['keyword_match_source'] ?? null)
                    ? trim((string) $row['keyword_match_source'])
                    : null,
                'match_reason' => filled($row['keyword_match_reason'] ?? null)
                    ? trim((string) $row['keyword_match_reason'])
                    : null,
            ], fn (mixed $value): bool => $value !== null);
        }

        if (filled($row['popularity_note'] ?? null)) {
            $formatted['popularity_note'] = trim((string) $row['popularity_note']);
        }

        return $formatted;
    }

    /**
     * @param  array<string, mixed>  $row
     * @return array<string, mixed>
     */
    private function exampleRow(
        string $retailer,
        string $name,
        string $url,
        string $price,
        ?string $listPrice,
        ?string $rating,
        ?string $reviewCount,
        string $imageUrl,
        string $keywordPrimary,
        array $keywordRelated = [],
    ): array {
        return [
            'retailer' => $retailer,
            'name' => $name,
            'url' => $url,
            'price' => $price,
            'list_price' => $listPrice,
            'rating' => $rating,
            'review_count' => $reviewCount,
            'image_url' => $imageUrl,
            'keyword_primary' => $keywordPrimary,
            'keyword_related' => $keywordRelated,
            'popularity_note' => filled($rating) && filled($reviewCount)
                ? trim((string) $rating).' stars, '.trim((string) $reviewCount).' reviews'
                : 'Imported product',
        ];
    }

    /**
     * @param  array<string, mixed>  $row
     * @return array<string, mixed>
     */
    private function stripInternalFields(array $row): array
    {
        unset($row['discovery_source'], $row['retailer']);

        return $row;
    }

    /**
     * @param  array<string, mixed>|null  $generatedSeeds
     */
    private function hasGeneratedSeeds(?array $generatedSeeds): bool
    {
        if ($generatedSeeds === null) {
            return false;
        }

        foreach (['user', 'ai', 'heuristic'] as $source) {
            if (($generatedSeeds[$source] ?? []) !== []) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param  array<string, mixed>|null  $generatedSeeds
     * @return array{user?: list<string>, ai?: list<string>, heuristic?: list<string>}|null
     */
    private function formatGeneratedSeeds(?array $generatedSeeds): ?array
    {
        if ($generatedSeeds === null) {
            return null;
        }

        $formatted = array_filter([
            'user' => array_values(array_filter(
                is_array($generatedSeeds['user'] ?? null) ? $generatedSeeds['user'] : [],
                fn (mixed $seed): bool => filled($seed),
            )),
            'ai' => array_values(array_filter(
                is_array($generatedSeeds['ai'] ?? null) ? $generatedSeeds['ai'] : [],
                fn (mixed $seed): bool => filled($seed),
            )),
            'heuristic' => array_values(array_filter(
                is_array($generatedSeeds['heuristic'] ?? null) ? $generatedSeeds['heuristic'] : [],
                fn (mixed $seed): bool => filled($seed),
            )),
        ], fn (array $seeds): bool => $seeds !== []);

        return $formatted === [] ? null : $formatted;
    }
}
