package org.raddatz.familienarchiv.audit;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

public interface AuditLogQueryRepository extends JpaRepository<AuditLog, UUID> {

    @Query(value = """
            SELECT a.document_id
            FROM audit_log a
            WHERE a.kind IN ('TEXT_SAVED', 'ANNOTATION_CREATED')
              AND a.actor_id = :userId
              AND a.document_id IS NOT NULL
            ORDER BY a.happened_at DESC
            LIMIT 1
            """, nativeQuery = true)
    Optional<UUID> findMostRecentDocumentIdByActor(@Param("userId") UUID userId);

    @Query(value = """
            WITH events AS (
                SELECT
                    a.kind,
                    a.actor_id,
                    a.document_id,
                    a.happened_at,
                    a.payload,
                    LAG(a.happened_at) OVER (
                        PARTITION BY a.actor_id, a.document_id, a.kind
                        ORDER BY a.happened_at
                    ) AS prev_happened_at
                FROM audit_log a
                WHERE a.kind IN (:kinds)
                  AND a.document_id IS NOT NULL
            ),
            sessions_marked AS (
                SELECT
                    kind, actor_id, document_id, happened_at, payload,
                    CASE
                        WHEN kind IN ('COMMENT_ADDED','MENTION_CREATED') THEN 1
                        WHEN prev_happened_at IS NULL THEN 1
                        WHEN EXTRACT(EPOCH FROM (happened_at - prev_happened_at)) > 7200 THEN 1
                        ELSE 0
                    END AS is_new_session
                FROM events
            ),
            sessions AS (
                SELECT
                    kind, actor_id, document_id, happened_at, payload,
                    SUM(is_new_session) OVER (
                        PARTITION BY actor_id, document_id, kind
                        ORDER BY happened_at
                        ROWS UNBOUNDED PRECEDING
                    ) AS session_id
                FROM sessions_marked
            ),
            aggregated AS (
                SELECT
                    s.kind,
                    s.actor_id,
                    s.document_id,
                    s.session_id,
                    MIN(s.happened_at)                                            AS happened_at,
                    CASE WHEN COUNT(*) > 1 THEN MAX(s.happened_at) ELSE NULL END  AS happened_at_until,
                    COUNT(*)::int                                                 AS count,
                    BOOL_OR(s.kind = 'MENTION_CREATED'
                        AND s.payload->>'mentionedUserId' = :currentUserId)       AS you_mentioned,
                    -- COMMENT_ADDED/MENTION_CREATED always have is_new_session=1, so each group has one row and MIN collapses to that row payload
                    MIN(s.payload::text)::jsonb                                   AS payload
                FROM sessions s
                GROUP BY s.kind, s.actor_id, s.document_id, s.session_id
            )
            SELECT
                ag.kind                                                AS kind,
                ag.actor_id                                            AS actorId,
                CASE
                    WHEN u.first_name IS NOT NULL AND u.last_name IS NOT NULL
                        THEN UPPER(LEFT(u.first_name, 1)) || UPPER(LEFT(u.last_name, 1))
                    WHEN u.first_name IS NOT NULL THEN UPPER(LEFT(u.first_name, 1))
                    WHEN u.last_name  IS NOT NULL THEN UPPER(LEFT(u.last_name, 1))
                    ELSE '?'
                END                                                    AS actorInitials,
                COALESCE(u.color, '')                                  AS actorColor,
                CONCAT_WS(' ', u.first_name, u.last_name)             AS actorName,
                ag.document_id                                         AS documentId,
                ag.happened_at                                         AS happened_at,
                ag.you_mentioned                                       AS youMentioned,
                -- payload->>'commentId' matches notifications.reference_id per AuditKind.COMMENT_ADDED contract
                EXISTS(
                    SELECT 1 FROM notifications n
                    WHERE n.type = 'REPLY'
                      AND n.recipient_id = CAST(:currentUserId AS uuid)
                      AND n.reference_id = (ag.payload->>'commentId')::uuid
                )                                                      AS youParticipated,
                ag.count                                               AS count,
                ag.happened_at_until                                   AS happenedAtUntil,
                (ag.payload->>'commentId')::uuid                       AS commentId
            FROM aggregated ag
            LEFT JOIN app_users u ON u.id = ag.actor_id
            ORDER BY ag.happened_at DESC
            LIMIT :limit
            """, nativeQuery = true)
    List<ActivityFeedRow> findRolledUpActivityFeed(
            @Param("currentUserId") String currentUserId,
            @Param("limit") int limit,
            @Param("kinds") Collection<String> kinds);

    @Query(value = """
            SELECT
                COUNT(DISTINCT (a.document_id::text || '|' || (a.payload->>'pageNumber'))) AS pages,
                COUNT(*) FILTER (WHERE a.kind = 'ANNOTATION_CREATED')                     AS annotated,
                COUNT(DISTINCT a.payload->>'blockId') FILTER (WHERE a.kind = 'TEXT_SAVED') AS transcribed,
                COUNT(DISTINCT a.document_id) FILTER (WHERE a.kind = 'FILE_UPLOADED')      AS uploaded,
                COUNT(DISTINCT (a.document_id::text || '|' || (a.payload->>'pageNumber')))
                    FILTER (WHERE (a.kind = 'ANNOTATION_CREATED' OR a.kind = 'TEXT_SAVED')
                            AND a.actor_id::text = :userId)                                AS yourPages
            FROM audit_log a
            WHERE a.happened_at >= :weekStart
              AND a.kind IN ('ANNOTATION_CREATED','TEXT_SAVED','FILE_UPLOADED')
            """, nativeQuery = true)
    PulseStatsRow getPulseStats(
            @Param("weekStart") OffsetDateTime weekStart,
            @Param("userId") String userId);

    @Query(value = """
            SELECT DISTINCT ON (a.document_id)
                a.document_id AS documentId,
                a.actor_id    AS actorId
            FROM audit_log a
            WHERE a.kind = :kind
              AND a.document_id IN :documentIds
              AND a.actor_id IS NOT NULL
            ORDER BY a.document_id, a.happened_at DESC
            """, nativeQuery = true)
    List<Object[]> findMostRecentActorPerDocument(
            @Param("documentIds") List<UUID> documentIds,
            @Param("kind") String kind);

    @Query(value = """
            SELECT
                a.document_id                                                    AS documentId,
                CASE
                    WHEN u.first_name IS NOT NULL AND u.last_name IS NOT NULL
                        THEN UPPER(LEFT(u.first_name, 1)) || UPPER(LEFT(u.last_name, 1))
                    WHEN u.first_name IS NOT NULL THEN UPPER(LEFT(u.first_name, 1))
                    WHEN u.last_name  IS NOT NULL THEN UPPER(LEFT(u.last_name, 1))
                    ELSE '?'
                END                                                              AS actorInitials,
                COALESCE(u.color, '')                                            AS actorColor,
                CONCAT_WS(' ', u.first_name, u.last_name)                       AS actorName
            FROM audit_log a
            LEFT JOIN app_users u ON u.id = a.actor_id
            WHERE a.kind IN ('ANNOTATION_CREATED', 'TEXT_SAVED', 'BLOCK_REVIEWED')
              AND a.document_id IN :documentIds
              AND a.actor_id IS NOT NULL
            GROUP BY a.document_id, a.actor_id, u.first_name, u.last_name, u.color
            ORDER BY a.document_id, MIN(a.happened_at)
            """, nativeQuery = true)
    List<ContributorRow> findContributorsPerDocument(@Param("documentIds") List<UUID> documentIds);

    @Query(value = """
            SELECT
                ranked.document_id   AS documentId,
                ranked.actorInitials AS actorInitials,
                ranked.actorColor    AS actorColor,
                ranked.actorName     AS actorName
            FROM (
                SELECT
                    a.document_id,
                    CASE
                        WHEN u.first_name IS NOT NULL AND u.last_name IS NOT NULL
                            THEN UPPER(LEFT(u.first_name, 1)) || UPPER(LEFT(u.last_name, 1))
                        WHEN u.first_name IS NOT NULL THEN UPPER(LEFT(u.first_name, 1))
                        WHEN u.last_name  IS NOT NULL THEN UPPER(LEFT(u.last_name, 1))
                        ELSE '?'
                    END                                                        AS actorInitials,
                    COALESCE(u.color, '')                                      AS actorColor,
                    NULLIF(CONCAT_WS(' ', u.first_name, u.last_name), '')     AS actorName,
                    ROW_NUMBER() OVER (
                        PARTITION BY a.document_id
                        ORDER BY MAX(a.happened_at) DESC
                    )                                                          AS rn
                FROM audit_log a
                LEFT JOIN app_users u ON u.id = a.actor_id
                WHERE a.kind IN ('ANNOTATION_CREATED', 'TEXT_SAVED', 'BLOCK_REVIEWED')
                  AND a.document_id IN :documentIds
                  AND a.actor_id IS NOT NULL
                GROUP BY a.document_id, a.actor_id, u.first_name, u.last_name, u.color
            ) ranked
            WHERE ranked.rn <= 4
            ORDER BY ranked.document_id, ranked.rn
            """, nativeQuery = true)
    List<ContributorRow> findRecentContributorsForDocuments(@Param("documentIds") List<UUID> documentIds);

    Page<AuditLog> findByKindIn(Collection<AuditKind> kinds, Pageable pageable);
}
