<?php

namespace App\Services;

use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class KnowledgeServiceApi
{
    public function __construct(
        protected string $baseUrl = '',
        protected int $timeout = 10,
        protected int $cacheTtl = 300,
    ) {
        $this->baseUrl = rtrim($this->baseUrl ?: config('knowledge.base_url'), '/');
        $this->timeout = $this->timeout ?: (int) config('knowledge.timeout', 10);
        $this->cacheTtl = $this->cacheTtl ?: (int) config('knowledge.cache_ttl', 300);
    }

    /**
     * Fetch and cache all knowledge points by iterating through paginated API responses.
     *
     * @return Collection<int, array<string, mixed>>
     */
    public function listKnowledgePoints(int $perPage = 200, array $filters = []): Collection
    {
        $cacheKey = sprintf(
            'knowledge-points-all-%d-%s',
            $perPage,
            md5(json_encode($filters))
        );

        return Cache::remember(
            $cacheKey,
            now()->addSeconds($this->cacheTtl),
            function () use ($perPage, $filters): Collection {
                $page = 1;
                $results = collect();

                while (true) {
                    $response = $this->paginateKnowledgePoints($page, $perPage, $filters);
                    $chunk = collect($response['data'] ?? []);

                    if ($chunk->isEmpty()) {
                        break;
                    }

                    $results = $results->concat($chunk);

                    $meta = $response['meta'] ?? [];
                    $hasNext = $meta['has_next'] ?? ($page < ($meta['total_pages'] ?? $page));
                    if (! $hasNext) {
                        break;
                    }

                    $page++;
                }

                return $results->values();
            },
        );
    }

    /**
     * @return array{data: array<int, array<string, mixed>>, meta: array<string, mixed>}
     */
    public function paginateKnowledgePoints(int $page = 1, int $perPage = 50, array $filters = []): array
    {
        $query = array_filter([
            'page' => $page,
            'per_page' => $perPage,
            'phase' => $filters['phase'] ?? null,
            'category' => $filters['category'] ?? null,
            'grade' => $filters['grade'] ?? null,
            'search' => $filters['search'] ?? null,
        ], fn ($value) => filled($value));

        $response = $this->request('GET', '/knowledge-points', $query);

        if (is_array($response) && array_key_exists('data', $response)) {
            return [
                'data' => $response['data'] ?? [],
                'meta' => $response['meta'] ?? [],
            ];
        }

        return [
            'data' => is_array($response) ? $response : [],
            'meta' => [
                'page' => $page,
                'per_page' => $perPage,
                'total' => is_array($response) ? count($response) : 0,
                'total_pages' => 1,
                'has_next' => false,
                'has_prev' => $page > 1,
            ],
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function getKnowledgePointDetail(string $kpCode): array
    {
        return Cache::remember(
            key: "knowledge-point-detail-{$kpCode}",
            ttl: now()->addSeconds($this->cacheTtl),
            callback: fn () => $this->request('GET', "/graph/node/{$kpCode}"),
        );
    }

    /**
     * 获取包含上下游节点的完整图谱数据
     */
    public function getFullGraphData(string $kpCode): array
    {
        $data = $this->getKnowledgePointDetail($kpCode);

        if (empty($data)) {
            return $data;
        }

        $data['child_nodes'] = $this->findChildNodes($kpCode);

        $data['parent_nodes'] = [];
        if (!empty($data['parents']) && is_array($data['parents'])) {
            foreach ($data['parents'] as $parentCode) {
                $parentDetail = $this->getKnowledgePointDetail($parentCode);
                if ($parentDetail) {
                    $data['parent_nodes'][] = $parentDetail;
                }
            }
        }

        return $data;
    }

    /**
     * 反向查找子节点（以当前节点为父节点的节点）
     */
    private function findChildNodes(string $kpCode): array
    {
        // 缓存键
        $cacheKey = "knowledge-point-children-{$kpCode}";

        return Cache::remember(
            key: $cacheKey,
            ttl: now()->addSeconds($this->cacheTtl),
            callback: function () use ($kpCode) {
                try {
                    \Log::info("开始查找子节点: {$kpCode}");

                    // 获取所有知识点
                    $allPoints = $this->listKnowledgePoints();

                    \Log::info("获取到知识点数量: " . $allPoints->count());

                    // 查找以当前节点为父节点的知识点
                    $children = $allPoints->filter(function ($point) use ($kpCode) {
                        return in_array($kpCode, $point['parents'] ?? []);
                    })->values()->toArray();

                    \Log::info("找到子节点数量: " . count($children));

                    return $children;
                } catch (\Exception $e) {
                    \Log::error("查找子节点失败: " . $e->getMessage());
                    \Log::error($e->getTraceAsString());
                    return [];
                }
            }
        );
    }

    /**
     * @return Collection<int, array<string, mixed>>
     */
    public function listSkills(?string $kpCode = null, int $limit = 200): Collection
    {
        return Cache::remember(
            key: "knowledge-skills-{$kpCode}-{$limit}",
            ttl: now()->addSeconds($this->cacheTtl),
            callback: fn () => collect($this->request('GET', '/skills', array_filter([
                'kp_code' => $kpCode,
                'limit' => $limit,
            ]))),
        );
    }

    /**
     * Search knowledge points by keyword
     *
     * @return Collection<int, array<string, mixed>>
     */
    public function searchKnowledgePoints(string $keyword, int $limit = 50): Collection
    {
        // Ensure keyword is not empty
        $keyword = trim($keyword);
        if ($keyword === '') {
            return collect();
        }

        // Use query params array for proper encoding (no cache during debugging)
        try {
            $response = $this->request('GET', 'knowledge-points/search', [
                'keyword' => $keyword,
                'limit' => $limit,
            ]);
        } catch (\Exception $e) {
            // Log error but don't fail completely
            \Log::error('Search API error: ' . $e->getMessage());
            return collect();
        }

        return collect($response);
    }

    /**
     * @throws RequestException
     * @return mixed
     */
    protected function request(string $method, string $path, array $params = []): mixed
    {
        $response = $this->http()->send($method, ltrim($path, '/'), [
            'query' => $params,
        ]);

        return $response->throw()->json();
    }

    protected function http(): PendingRequest
    {
        return Http::baseUrl($this->baseUrl)
            ->acceptJson()
            ->timeout($this->timeout)
            ->retry(2, 200)
            ->withHeaders([
                'User-Agent' => 'Laravel/' . (app()->version()),
                'Accept' => 'application/json',
            ]);
    }
}
