<?php

namespace app\controller\Api;

use app\service\ArchiveRepository;
use support\Response;
use Throwable;

class EvidenceController
{
    public function archive(string $archiveUid): Response
    {
        try {
            $archive = (new ArchiveRepository())->findArchive($archiveUid);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Archive lookup failed.',
                'errors' => ['archive' => $exception->getMessage()],
            ], 500);
        }

        if ($archive === null) {
            return $this->jsonResponse([
                'code' => 404,
                'message' => 'Archive not found.',
                'errors' => ['archive_uid' => $archiveUid],
            ], 404);
        }

        $archive['chunk_count'] = is_array($archive['chunks'] ?? null) ? count($archive['chunks']) : 0;

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Archive loaded.',
            'data' => $archive,
        ], 200);
    }

    public function chunk(string $chunkUid): Response
    {
        try {
            $chunk = (new ArchiveRepository())->findChunk($chunkUid);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Chunk lookup failed.',
                'errors' => ['chunk' => $exception->getMessage()],
            ], 500);
        }

        if ($chunk === null) {
            return $this->jsonResponse([
                'code' => 404,
                'message' => 'Chunk not found.',
                'errors' => ['chunk_uid' => $chunkUid],
            ], 404);
        }

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Chunk loaded.',
            'data' => $chunk,
        ], 200);
    }

    public function archiveChunks(string $archiveUid): Response
    {
        try {
            $repository = new ArchiveRepository();
            $archive = $repository->findArchive($archiveUid);
            $chunks = $archive === null ? [] : $repository->findArchiveChunks($archiveUid);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Archive chunks lookup failed.',
                'errors' => ['archive_chunks' => $exception->getMessage()],
            ], 500);
        }

        if ($archive === null) {
            return $this->jsonResponse([
                'code' => 404,
                'message' => 'Archive not found.',
                'errors' => ['archive_uid' => $archiveUid],
            ], 404);
        }

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Archive chunks loaded.',
            'data' => [
                'archive_uid' => $archive['archive_uid'],
                'title' => $archive['title'],
                'summary' => $archive['summary'],
                'source' => $archive['source'],
                'author' => $archive['author'],
                'year' => $archive['year'],
                'series' => $archive['series'],
                'tags' => $archive['tags'],
                'chunk_count' => count($chunks),
                'chunks' => $chunks,
            ],
        ], 200);
    }

    public function evidence(string $chunkUid): Response
    {
        try {
            $chunk = (new ArchiveRepository())->findChunk($chunkUid);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Evidence lookup failed.',
                'errors' => ['evidence' => $exception->getMessage()],
            ], 500);
        }

        if ($chunk === null) {
            return $this->jsonResponse([
                'code' => 404,
                'message' => 'Evidence not found.',
                'errors' => ['chunk_uid' => $chunkUid],
            ], 404);
        }

        $archive = $chunk['archive'];
        $pages = $chunk['pages'];
        $pageLabel = $this->pageLabel($pages);

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Evidence loaded.',
            'data' => [
                'chunk_uid' => $chunk['chunk_uid'],
                'archive_uid' => $chunk['archive_uid'],
                'title' => $archive['title'] ?? null,
                'source' => $archive['source'] ?? null,
                'author' => $archive['author'] ?? null,
                'year' => $archive['year'] ?? null,
                'series' => $archive['series'] ?? null,
                'tags' => $archive['tags'] ?? [],
                'page_start' => $chunk['page_start'],
                'page_end' => $chunk['page_end'],
                'pages' => $pages,
                'page_label' => $pageLabel,
                'citation' => $this->citation($archive, $pageLabel),
                'quote' => $chunk['text'],
                'chunk' => [
                    'chunk_index' => $chunk['chunk_index'],
                    'length' => $chunk['length'],
                    'embedding_model' => $chunk['embedding_model'],
                    'embedding_status' => $chunk['embedding_status'],
                    'search_index_status' => $chunk['search_index_status'],
                ],
            ],
        ], 200);
    }

    public function archiveEvidence(string $archiveUid): Response
    {
        try {
            $repository = new ArchiveRepository();
            $archive = $repository->findArchive($archiveUid);
            $chunks = $archive === null ? [] : $repository->findArchiveChunks($archiveUid);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Archive evidence lookup failed.',
                'errors' => ['archive_evidence' => $exception->getMessage()],
            ], 500);
        }

        if ($archive === null) {
            return $this->jsonResponse([
                'code' => 404,
                'message' => 'Archive not found.',
                'errors' => ['archive_uid' => $archiveUid],
            ], 404);
        }

        $evidence = array_map(function (array $chunk): array {
            $archive = $chunk['archive'];
            $pages = $chunk['pages'];
            $pageLabel = $this->pageLabel($pages);

            return [
                'chunk_uid' => $chunk['chunk_uid'],
                'chunk_index' => $chunk['chunk_index'],
                'page_start' => $chunk['page_start'],
                'page_end' => $chunk['page_end'],
                'pages' => $pages,
                'page_label' => $pageLabel,
                'citation' => $this->citation($archive, $pageLabel),
                'quote' => $chunk['text'],
                'length' => $chunk['length'],
                'embedding_model' => $chunk['embedding_model'],
                'embedding_status' => $chunk['embedding_status'],
                'search_index_status' => $chunk['search_index_status'],
            ];
        }, $chunks);

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Archive evidence loaded.',
            'data' => [
                'archive_uid' => $archive['archive_uid'],
                'title' => $archive['title'],
                'summary' => $archive['summary'],
                'source' => $archive['source'],
                'author' => $archive['author'],
                'year' => $archive['year'],
                'series' => $archive['series'],
                'tags' => $archive['tags'],
                'chunk_count' => count($evidence),
                'evidence' => $evidence,
            ],
        ], 200);
    }

    private function citation(array $archive, string $pageLabel): string
    {
        $parts = array_values(array_filter([
            $archive['title'] ?? null,
            $archive['author'] ?? null,
            isset($archive['year']) ? (string) $archive['year'] : null,
            $pageLabel === '' ? null : $pageLabel,
            $archive['source'] ?? null,
        ], static fn ($value): bool => $value !== null && trim((string) $value) !== ''));

        return implode(' | ', $parts);
    }

    private function pageLabel(array $pages): string
    {
        if ($pages === []) {
            return '';
        }

        if (count($pages) === 1) {
            return 'p. ' . (string) $pages[0];
        }

        return 'pp. ' . (string) $pages[0] . '-' . (string) $pages[count($pages) - 1];
    }

    private function jsonResponse(array $data, int $status): Response
    {
        return response(
            json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
            $status,
            ['Content-Type' => 'application/json']
        );
    }
}
