<?php

namespace ZenoChatbot\Services\LLMService\AIClient\Results;

if (!defined('ABSPATH')) {
    exit;
}

class GenerativeAiResult
{
    /** @var Candidate[] */
    private array $candidates = [];
    private ?TokenUsage $tokenUsage = null;
    private ?string $finishReason = null;
    private array $citations = [];
    private ?string $provider = null;
    private ?string $model = null;

    /**
     * Raw API response from the provider
     * @var array|null
     */
    private ?array $rawResponse = null;

    /**
     * Raw API request payload sent to the provider
     * @var array|null
     */
    private ?array $rawRequest = null;

    /**
     * Files skipped due to size limits or unsupported types
     * @var array
     */
    private array $skippedFiles = [];

    public function __construct(
        array $candidates = [],
        ?TokenUsage $usage = null,
        ?string $finishReason = null,
        array $citations = [],
        ?string $provider = null,
        ?string $model = null,
        ?array $rawResponse = null,
        ?array $rawRequest = null,
        array $skippedFiles = []
    ) {
        $this->candidates = $candidates;
        $this->tokenUsage = $usage;
        $this->finishReason = $finishReason;
        $this->citations = $citations;
        $this->provider = $provider;
        $this->model = $model;
        $this->rawResponse = $rawResponse;
        $this->rawRequest = $rawRequest;
        $this->skippedFiles = $skippedFiles;
    }

    /**
     * Create a new GenerativeAiResult from an associative array.
     *
     * Provides a cleaner alternative to the multi-parameter constructor.
     *
     * @param array $data {
     *     @type Candidate[] $candidates    Response candidates
     *     @type TokenUsage  $token_usage   Token usage statistics
     *     @type string      $finish_reason Why generation stopped
     *     @type array       $citations     Source citations
     *     @type string      $provider      Provider name (e.g., 'openai', 'gemini')
     *     @type string      $model         Model name
     *     @type array       $raw_response  Raw API response
     *     @type array       $raw_request   Raw API request
     *     @type array       $skipped_files Files skipped during processing
     * }
     * @return static
     */
    /** @return self */
    public static function create(array $data): self
    {
        return new static(
            $data['candidates'] ?? [],
            $data['token_usage'] ?? null,
            $data['finish_reason'] ?? null,
            $data['citations'] ?? [],
            $data['provider'] ?? null,
            $data['model'] ?? null,
            $data['raw_response'] ?? null,
            $data['raw_request'] ?? null,
            $data['skipped_files'] ?? []
        );
    }

    public function toText(): string
    {
        $first = $this->candidates[0] ?? null;
        return $first ? $first->getText() : '';
    }

    /**
     * @return string[]
     */
    public function toTexts(): array
    {
        return array_map(static fn($c) => $c->getText(), $this->candidates);
    }

    public function getCitations(): array
    {
        return $this->citations;
    }

    public function getFinishReason(): ?string
    {
        return $this->finishReason;
    }

    public function getTokenUsage(): ?TokenUsage
    {
        return $this->tokenUsage;
    }

    /**
     * Get the raw API response from the provider
     *
     * @return array|null
     */
    public function getRawResponse(): ?array
    {
        return $this->rawResponse;
    }

    /**
     * Get the raw API request payload sent to the provider
     *
     * @return array|null
     */
    public function getRawRequest(): ?array
    {
        return $this->rawRequest;
    }

    /**
     * Get the provider name
     *
     * @return string|null
     */
    public function getProvider(): ?string
    {
        return $this->provider;
    }

    /**
     * Get the model name
     *
     * @return string|null
     */
    public function getModel(): ?string
    {
        return $this->model;
    }

    /**
     * Get files that were skipped due to size limits or unsupported types
     *
     * @return array
     */
    public function getSkippedFiles(): array
    {
        return $this->skippedFiles;
    }

    /**
     * Check if any files were skipped
     *
     * @return bool
     */
    public function hasSkippedFiles(): bool
    {
        return !empty($this->skippedFiles);
    }

    /**
     * Get skipped filenames as a simple array
     *
     * @return array
     */
    public function getSkippedFilenames(): array
    {
        return array_column($this->skippedFiles, 'filename');
    }

    public function toArray(): array
    {
        $arr = [
            'provider'      => $this->provider,
            'model'         => $this->model,
            'finish_reason' => $this->finishReason,
            'citations'     => $this->citations,
            'token_usage'   => $this->tokenUsage ? $this->tokenUsage->toArray() : null,
            'candidates'    => $this->toTexts(),
        ];

        if (!empty($this->skippedFiles)) {
            $arr['skipped_files'] = $this->skippedFiles;
        }

        return $arr;
    }

    /**
     * Get full data including raw response/request for debugging
     *
     * @return array
     */
    public function toFullArray(): array
    {
        return [
            'provider'      => $this->provider,
            'model'         => $this->model,
            'finish_reason' => $this->finishReason,
            'citations'     => $this->citations,
            'token_usage'   => $this->tokenUsage ? $this->tokenUsage->toArray() : null,
            'candidates'    => $this->toTexts(),
            'raw_response'  => $this->rawResponse,
            'raw_request'   => $this->rawRequest,
            'skipped_files' => $this->skippedFiles,
        ];
    }
}

