<?php

namespace App\Services\Keywords;

use App\Services\AI\TextProviderFailover;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Throwable;

use function Laravel\Ai\agent;

class AiKeywordSeedBuilder
{
    public function __construct(private ProductKeywordParser $keywordParser) {}

    /**
     * @param  list<array<string, mixed>>  $candidates
     * @return array{
     *     seeds: list<string>,
     *     by_product: list<list<string>>,
     * }
     */
    public function build(
        string $brandName,
        string $category,
        string $subcategory,
        array $candidates,
    ): array {
        if ($candidates === [] || ! config('google-ads.ai_seed_generation', true)) {
            return [
                'seeds' => [],
                'by_product' => array_fill(0, count($candidates), []),
            ];
        }

        try {
            $scope = trim(collect([$category, $subcategory])->filter()->implode(' / '));
            $scopeLine = $scope !== '' ? "Category scope: {$scope}\n" : '';
            $brandLine = trim($brandName) !== '' ? "Brand: {$brandName}\n" : '';
            $limit = max(1, min(5, (int) config('google-ads.max_seeds', 15)));

            $prompt = "{$brandLine}{$scopeLine}"
                ."Suggest Google Search keyword seeds shoppers would use for each product below.\n"
                ."Return short search phrases (2-5 words). Avoid retailer/store names unless the product is brand-specific.\n"
                ."Prefer buyer-intent terms over model numbers.\n"
                ."Return up to {$limit} unique seeds per product.\n\n"
                ."Products:\n".$this->productBlock($candidates);

            $response = agent(
                instructions: 'You generate Google Ads keyword planner seed phrases for ecommerce catalog products. Output only realistic search terms per product index.',
                schema: fn (JsonSchema $schema) => [
                    'products' => $schema->array()->items(
                        $schema->object([
                            'index' => $schema->integer()->description('Product index from the candidate list.')->required(),
                            'seeds' => $schema->array()
                                ->items($schema->string())
                                ->description('Unique lowercase keyword seed phrases for this product.')
                                ->required(),
                        ])
                    )->description('One row per product index.')->required(),
                ],
            )->prompt($prompt, provider: TextProviderFailover::providers());

            $byProduct = $this->parseProductSeeds(
                $response->toArray()['products'] ?? [],
                count($candidates),
            );

            Log::info('Catalog AI keyword seeds generated', [
                'seed_count' => count($this->flattenProductSeeds($byProduct)),
                'candidate_count' => count($candidates),
            ]);

            return [
                'seeds' => $this->flattenProductSeeds($byProduct),
                'by_product' => $byProduct,
            ];
        } catch (Throwable $exception) {
            Log::warning('Catalog AI keyword seed generation failed', [
                'candidate_count' => count($candidates),
                'error' => $exception->getMessage(),
            ]);

            return [
                'seeds' => [],
                'by_product' => array_fill(0, count($candidates), []),
            ];
        }
    }

    /**
     * @param  list<array<string, mixed>>  $candidates
     */
    private function productBlock(array $candidates): string
    {
        return collect($candidates)
            ->take(12)
            ->map(function (array $candidate, int $index): string {
                $keywords = $this->keywordParser->productKeywordsFromRow($candidate);
                $payload = [
                    'index' => $index + 1,
                    'name' => $candidate['name'] ?? '',
                    'url' => $candidate['url'] ?? '',
                    'price' => $candidate['price'] ?? null,
                    'product_keywords' => $keywords,
                ];

                return json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: '{}';
            })
            ->implode("\n");
    }

    /**
     * @param  list<mixed>  $rows
     * @return list<list<string>>
     */
    private function parseProductSeeds(array $rows, int $candidateCount): array
    {
        $byProduct = array_fill(0, $candidateCount, []);

        foreach ($rows as $row) {
            if (! is_array($row)) {
                continue;
            }

            $index = (int) ($row['index'] ?? 0) - 1;

            if ($index < 0 || $index >= $candidateCount) {
                continue;
            }

            $byProduct[$index] = $this->normalizeSeeds($row['seeds'] ?? []);
        }

        return $byProduct;
    }

    /**
     * @param  list<list<string>>  $byProduct
     * @return list<string>
     */
    private function flattenProductSeeds(array $byProduct): array
    {
        $flattened = [];

        foreach ($byProduct as $seeds) {
            foreach ($seeds as $seed) {
                if ($seed === '' || in_array($seed, $flattened, true)) {
                    continue;
                }

                $flattened[] = $seed;
            }
        }

        return $flattened;
    }

    /**
     * @param  list<mixed>  $seeds
     * @return list<string>
     */
    private function normalizeSeeds(array $seeds): array
    {
        $normalized = [];

        foreach ($seeds as $seed) {
            $value = trim(preg_replace('/\s+/', ' ', Str::lower((string) $seed)) ?? '');

            if ($value === '' || in_array($value, $normalized, true)) {
                continue;
            }

            $normalized[] = $value;
        }

        return $normalized;
    }
}
