CREATE OR REPLACE FUNCTION listDocumentIDs(
    _clientId varchar(255),
    _batchSize INTEGER,
    _offset INTEGER
)
RETURNS TABLE (id uuid, totalCount BIGINT) AS $$
DECLARE
    totalCount BIGINT := 0;
BEGIN
    SELECT COUNT(*)::BIGINT INTO totalCount FROM documents WHERE clientId = _clientId;

    RETURN QUERY
        SELECT d.id, totalCount
        FROM documents as d
        WHERE d.clientId = _clientId
        ORDER BY d.id
        LIMIT _batchSize
        OFFSET _offset;
END;
$$ LANGUAGE plpgsql;

CREATE VIEW currentCleanEntries as
WITH RankedExtractions AS (
    SELECT
        dc.id,
        dc.documentId,
        dc.bucket,
        dc.key,
        dc.mimetype,
        dc.fail,
        dc.hash,
        dce.version,
        d.clientId,
        ROW_NUMBER() OVER (PARTITION BY dc.documentId ORDER BY dc.id DESC, dce.id DESC) as row_num
    FROM documents d
    JOIN currentCollectorMinCleanVersions ccv ON d.clientId = ccv.clientId
    JOIN documentCleans dc ON dc.documentId = d.id
    JOIN documentCleanEntries dce ON dc.id = dce.cleanId
        AND dce.version >= ccv.minCleanVersion
)
SELECT
    re.id,
    re.documentId,
    re.bucket,
    re.key,
    re.version,
    re.hash,
    re.mimetype,
    re.fail,
    re.clientId
FROM RankedExtractions re
WHERE row_num = 1;

CREATE VIEW currentTextEntries as
WITH RankedExtractions AS (
    SELECT
        extract.id,
        cc.documentId,
        extract.bucket,
        extract.key,
        extract.hash,
        extract.cleanId,
        dtee.version as extractionVersion,
        ROW_NUMBER() OVER (PARTITION BY cc.documentId ORDER BY dtee.id DESC) as row_num
    FROM documents d
    JOIN currentCleanEntries cc on cc.documentId = d.id
    JOIN currentCollectorMinTextVersions ctv ON ctv.clientId = d.clientId
    JOIN documentTextExtractions extract ON extract.cleanId = cc.id
    JOIN documentTextExtractionEntries dtee on dtee.textId = extract.id
        AND dtee.version >= ctv.minTextVersion
)
SELECT
    id,
    documentId,
    bucket,
    key,
    hash,
    cleanId,
    extractionVersion
FROM RankedExtractions
WHERE row_num = 1;

CREATE VIEW currentClientCanSync as
WITH RankedExtractions AS (
    SELECT
        ccs.clientId,
        ccs.canSync,
        ROW_NUMBER() OVER (PARTITION BY ccs.clientId ORDER BY ccs.id DESC) as row_num
    FROM clients c
    JOIN clientCanSync ccs on c.id = ccs.clientId
)
SELECT
    c.id as clientId,
    coalesce(r.canSync, false) as canSync
FROM clients c
LEFT JOIN RankedExtractions r on r.clientId = c.id and r.row_num = 1;

CREATE VIEW fullClients as
SELECT c.id, c.name, cs.canSync
    FROM clients as c
    JOIN currentClientCanSync as cs on cs.clientId = c.id;

CREATE OR REPLACE FUNCTION listValidDocumentResults(doc_id uuid)
RETURNS TABLE (documentId uuid, queryId uuid, resultId uuid, value text) AS $$
DECLARE
 count_before INT;
 count_after INT;
 iterations INT := 0;
BEGIN
    CREATE TEMPORARY TABLE allResults AS
    WITH
        docs AS (
            SELECT id as documentId, hash, clientId
            FROM documents
            WHERE id = doc_id
    ),
    query_dependency_tree AS (
        WITH RECURSIVE query_deps AS (
            SELECT
            q.queryId,
            unnest(q.requiredIds) AS requiredId
            FROM collectorQueryDependencyTree AS q
            JOIN docs AS d ON d.clientId = q.clientId
            WHERE array_length(q.requiredIds, 1) > 0

            UNION ALL

            SELECT
            qd.queryId,
            unnest(q.requiredIds) AS requiredId
            FROM query_deps qd
            JOIN collectorQueryDependencyTree q ON qd.requiredId = q.queryId
            WHERE array_length(q.requiredIds, 1) > 0
        )
        SELECT
        query_deps.queryId,
        array_agg(DISTINCT requiredId) AS all_required_ids
        FROM query_deps
        GROUP BY query_deps.queryId
    ),
    docResults AS (
    SELECT
        d.documentId,
        q.queryId,
        r.id AS resultId,
        r.value,
        q.requiredIds as directRequiredIds,
        COALESCE(qdt.all_required_ids, ARRAY[]::uuid[]) AS allRequiredIds
        FROM docs AS d
        JOIN collectorQueryDependencyTree AS q ON q.clientId = d.clientId
        LEFT JOIN query_dependency_tree qdt ON q.queryId = qdt.queryId
        LEFT JOIN currentTextEntries AS tte ON tte.documentId = d.documentId
        LEFT JOIN results AS r
            ON q.queryId = r.queryId
            AND r.queryVersion = q.queryVersion
            AND tte.id = r.textEntryId
        WHERE r.value is not null
    )
    SELECT * FROM docResults dr;

    CREATE TEMPORARY TABLE finalResults AS
    SELECT * from allResults where array_length(directRequiredIds, 1) is null;

    SELECT COUNT(*) INTO count_before FROM finalResults;
    RAISE NOTICE 'Starting with % rows', count_before;

    LOOP
        iterations := iterations + 1;

        INSERT INTO finalResults
            SELECT dr.*
            FROM allResults dr
            WHERE
                dr.directRequiredIds IS NOT NULL
                AND NOT EXISTS (
                    SELECT 1
                    FROM unnest(dr.directRequiredIds) AS req_id(queryId)
                    WHERE NOT EXISTS (
                        SELECT 1
                        FROM allResults req_dr
                        WHERE req_dr.queryId = req_id.queryId
                        AND req_dr.resultId IN (SELECT fr.resultId FROM finalResults fr)
                        AND exists (
                            select 1 from resultDependencies rrd
                            where dr.resultId = rrd.resultId
                            and req_dr.resultId = rrd.requiredResultId
                        )
                    )
                )
                and dr.resultId NOT IN (SELECT fr.resultId FROM finalResults fr);

        GET DIAGNOSTICS count_after = ROW_COUNT;
        RAISE NOTICE 'Iteration %: Additional Entries %',
        iterations, count_after;

        IF count_after = 0 THEN
            EXIT;
        END IF;
    END LOOP;

    RETURN QUERY SELECT tr.documentId, tr.queryId, tr.resultId, tr.value
        FROM finalResults tr;

    DROP TABLE finalResults;
    DROP TABLE allResults;
END;
$$ LANGUAGE plpgsql;
