<?php

declare(strict_types=1);

namespace App\Tag;

final readonly class TagVectorIndexHealthService
{
    private const STATUS_OK = 'OK';
    private const STATUS_OK_EMPTY = 'OK_EMPTY';
    private const STATUS_INCONSISTENT_STALE_VECTOR = 'INCONSISTENT_STALE_VECTOR';
    private const STATUS_INCONSISTENT_MISSING_VECTOR = 'INCONSISTENT_MISSING_VECTOR';
    private const STATUS_INCONSISTENT_COUNT_MISMATCH = 'INCONSISTENT_COUNT_MISMATCH';
    private const STATUS_INCONSISTENT_INVALID_META = 'INCONSISTENT_INVALID_META';
    private const STATUS_UNKNOWN = 'UNKNOWN';

    public function __construct(
        private string $tagsNdjsonPath,
        private string $vectorTagsIndexPath,
        private string $vectorTagsMetaPath,
    ) {
    }

    public function check(): array
    {
        $ndjsonExists = is_file($this->tagsNdjsonPath);
        $vectorExists = is_file($this->vectorTagsIndexPath);
        $metaExists = is_file($this->vectorTagsMetaPath);

        $ndjsonStats = $this->readNdjsonStats();
        $metaStats = $this->readMetaStats();

        $status = $this->determineStatus(
            $ndjsonStats['exported_tag_count'],
            $vectorExists,
            $metaExists,
            $metaStats['vector_tag_count'],
            $metaStats['meta_valid']
        );

        return [
            'tags_ndjson_exists' => $ndjsonExists,
            'tags_ndjson_count' => $ndjsonStats['exported_tag_count'],
            'vector_exists' => $vectorExists,
            'meta_exists' => $metaExists,
            'vector_tag_count' => $metaStats['vector_tag_count'],
            'status' => $status,

            // Extra diagnostics for admin/CLI.
            'tags_ndjson_lines_total' => $ndjsonStats['lines_total'],
            'tags_ndjson_invalid_lines' => $ndjsonStats['invalid_lines'],
            'tags_ndjson_empty_lines' => $ndjsonStats['empty_lines'],
            'tags_with_active_document_ids' => $ndjsonStats['tags_with_document_ids'],
            'meta_valid' => $metaStats['meta_valid'],
            'paths' => [
                'tags_ndjson' => $this->tagsNdjsonPath,
                'vector_index' => $this->vectorTagsIndexPath,
                'vector_meta' => $this->vectorTagsMetaPath,
            ],
        ];
    }

    /**
     * @return array{
     *   lines_total:int,
     *   empty_lines:int,
     *   invalid_lines:int,
     *   exported_tag_count:int,
     *   tags_with_document_ids:int
     * }
     */
    private function readNdjsonStats(): array
    {
        $stats = [
            'lines_total' => 0,
            'empty_lines' => 0,
            'invalid_lines' => 0,
            'exported_tag_count' => 0,
            'tags_with_document_ids' => 0,
        ];

        if (!is_file($this->tagsNdjsonPath)) {
            return $stats;
        }

        $handle = @fopen($this->tagsNdjsonPath, 'rb');

        if ($handle === false) {
            return $stats;
        }

        try {
            while (($line = fgets($handle)) !== false) {
                $stats['lines_total']++;
                $line = trim($line);

                if ($line === '') {
                    $stats['empty_lines']++;
                    continue;
                }

                $data = json_decode($line, true);

                if (!is_array($data)) {
                    $stats['invalid_lines']++;
                    continue;
                }

                $tagId = trim((string) ($data['tag_id'] ?? ''));
                $text = trim((string) ($data['text'] ?? ''));
                $documentIds = $data['document_ids'] ?? null;
                $hasDocumentIds = is_array($documentIds) && $documentIds !== [];

                if ($tagId === '' || $text === '') {
                    $stats['invalid_lines']++;
                    continue;
                }

                $stats['exported_tag_count']++;

                if ($hasDocumentIds) {
                    $stats['tags_with_document_ids']++;
                }
            }
        } finally {
            fclose($handle);
        }

        return $stats;
    }

    /**
     * @return array{vector_tag_count:int, meta_valid:bool}
     */
    private function readMetaStats(): array
    {
        if (!is_file($this->vectorTagsMetaPath)) {
            return [
                'vector_tag_count' => 0,
                'meta_valid' => false,
            ];
        }

        $raw = file_get_contents($this->vectorTagsMetaPath);

        if (!is_string($raw) || trim($raw) === '') {
            return [
                'vector_tag_count' => 0,
                'meta_valid' => false,
            ];
        }

        $decoded = json_decode($raw, true);

        if (is_array($decoded)) {
            if (array_is_list($decoded)) {
                return [
                    'vector_tag_count' => count($decoded),
                    'meta_valid' => true,
                ];
            }

            $numericKeys = array_filter(
                array_keys($decoded),
                static fn (string|int $key): bool => is_string($key) && ctype_digit($key)
            );

            if ($numericKeys !== [] && count($numericKeys) === count($decoded)) {
                return [
                    'vector_tag_count' => count($decoded),
                    'meta_valid' => true,
                ];
            }
        }

        return [
            'vector_tag_count' => 0,
            'meta_valid' => false,
        ];
    }

    private function determineStatus(
        int $ndjsonTagCount,
        bool $vectorExists,
        bool $metaExists,
        int $vectorTagCount,
        bool $metaValid
    ): string {
        if ($ndjsonTagCount === 0 && !$vectorExists && !$metaExists) {
            return self::STATUS_OK_EMPTY;
        }

        if ($ndjsonTagCount === 0 && ($vectorExists || $metaExists)) {
            return self::STATUS_INCONSISTENT_STALE_VECTOR;
        }

        if ($ndjsonTagCount > 0 && (!$vectorExists || !$metaExists)) {
            return self::STATUS_INCONSISTENT_MISSING_VECTOR;
        }

        if ($metaExists && !$metaValid) {
            return self::STATUS_INCONSISTENT_INVALID_META;
        }

        if ($ndjsonTagCount > 0 && $vectorExists && $metaExists && $metaValid && $vectorTagCount === $ndjsonTagCount) {
            return self::STATUS_OK;
        }

        if ($ndjsonTagCount !== $vectorTagCount) {
            return self::STATUS_INCONSISTENT_COUNT_MISMATCH;
        }

        return self::STATUS_UNKNOWN;
    }
}