package com.kospo.talk.repository;

import com.kospo.talk.model.TalkMessage;
import com.kospo.talk.model.TalkMessageId;
import com.kospo.talk.payload.ITalkMessageDto;
import jakarta.transaction.Transactional;
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.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDateTime;
import java.util.List;

public interface TalkMessageRepository extends JpaRepository<TalkMessage, TalkMessageId> {
    List<TalkMessage> findByIdTalkId(String talkId);

    Page<TalkMessage> findByIdTalkId(String talkId, Pageable pageable);

    @Query(value="select * from talk_message where to_char(ins_date, 'YYYY-MM-DD') = :insDate and talk_id = :talkId order by ins_date asc", nativeQuery = true)
    List<TalkMessage> findByIdTalkIdAndIdInsDate(@Param("talkId") String talkId, @Param("insDate") String insDate);

    @Query(value="SELECT \n" +
            "talk_id,\n" +
            "sabun,\n" +
            "message,\n" +
            "ins_date,\n" +
            "ins_name,\n" +
            "search_yn,\n" +
            "row_number() over(order by ins_date) as line\n" +
            "FROM talk_message\n" +
            "WHERE talk_id = :talkId\n" +
            "  AND ins_date >= date_trunc('day', CURRENT_DATE - cast(:week as integer) * 7)\n" +
            "  AND ins_date < date_trunc('day',\n" +
            "                            CURRENT_DATE - ((cast(:week as integer) - 1) * 7) + 1) order by ins_date asc", nativeQuery = true)
    List<ITalkMessageDto> findByIdTalkIdAndWeekFirst(@Param("talkId") String talkId, @Param("week") String week);

    @Query(value="SELECT \n" +
            "talk_id,\n" +
            "sabun,\n" +
            "message,\n" +
            "ins_date,\n" +
            "ins_name,\n" +
            "row_number() over(order by ins_date) as line\n" +
            "FROM talk_message\n" +
            "WHERE talk_id = :talkId\n" +
            "  AND ins_date >= date_trunc('day', CURRENT_DATE - cast(:week as integer) * 7)\n" +
            "  AND ins_date < date_trunc('day',\n" +
            "                            CURRENT_DATE - (cast(:week as integer) - 1) * 7) order by ins_date asc", nativeQuery = true)
    List<ITalkMessageDto> findByIdTalkIdAndWeek(@Param("talkId") String talkId, @Param("week") String week);

//    @Query(value="select * from (\n" +
//            "    select talk_message.talk_id,\n" +
//            "                 sabun,\n" +
//            "                 message,\n" +
//            "                 ins_date,\n" +
//            "                 ins_name,\n" +
//            "                 row_number() over\n" +
//            "                     (order by ins_date desc) as r,\n" +
//            "                 row_number() over (\n" +
//            "                     order by ins_date) as line\n" +
//            "      from talk_message where talk_id = :talkId and (:insDate is null or ins_date > :insDate)\n" +
//            ") where r > :start and r <= :end order by ins_date", nativeQuery = true)
    @Query(value = "WITH filtered_messages AS (\n" +
            "    SELECT talk_id, sabun, message, ins_date, ins_name,\n" +
            "           row_number() over ( order by ins_date) as line, search_yn\n" +
            "    FROM talk_message\n" +
            "    WHERE talk_id = :talkId\n" +
            "--         and ins_date < now() + interval '1 days' order by line desc\n" +
            "      AND (ins_date is null or ins_date < :insDate ::timestamp)\n" +
            ")\n" +
            "SELECT *\n" +
            "FROM filtered_messages\n" +
            "ORDER BY ins_date desc limit :end offset :start", nativeQuery = true)
    List<ITalkMessageDto> findByLine(@Param("talkId") String talkId, @Param("start") Integer start, @Param("end") Integer end, @Param("insDate") String insDate);

    @Query(value = "WITH filtered_messages AS (\n" +
            "    SELECT talk_id, sabun, message, ins_date, ins_name,\n" +
            "           row_number() over ( order by ins_date) as line, search_yn\n" +
            "    FROM talk_message\n" +
            "    WHERE talk_id = :talkId\n" +
            "--         and ins_date < now() + interval '1 days' order by line desc\n" +
            "      AND (ins_date is null or ins_date < :insDate ::timestamp)\n" +
            ")\n" +
            "SELECT *\n" +
            "FROM filtered_messages\n" +
            "ORDER BY ins_date asc limit :end offset :start", nativeQuery = true)
    List<ITalkMessageDto> findByLineAsc(@Param("talkId") String talkId, @Param("start") Integer start, @Param("end") Integer end, @Param("insDate") String insDate);

    @Query(value = "WITH filtered_messages AS (\n" +
            "    SELECT talk_id, sabun, message, ins_date, ins_name,\n" +
            "           row_number() over ( order by ins_date) as line, search_yn\n" +
            "    FROM talk_message\n" +
            "    WHERE talk_id = :talkId\n" +
            "     AND (ins_date is null or ins_date > :insDate ::timestamp)\n" +
            ")\n" +
            "SELECT *\n" +
            "FROM filtered_messages\n" +
            "ORDER BY ins_date limit :end offset :start", nativeQuery = true)
    List<ITalkMessageDto> findByLineMore(@Param("talkId") String talkId, @Param("start") Integer start, @Param("end") Integer end, @Param("insDate") String insDate);

    @Query(value="select *\n" +
            "from (select talk_id,\n" +
            "             sabun,\n" +
            "             message,\n" +
            "             ins_date,\n" +
            "             ins_name,\n" +
            "             row_number() over (order by ins_date) as line,\n" +
            "             search_yn\n" +
            "      from talk_message)\n" +
            "where talk_id = :talkId\n" +
            "  and ins_date >= (select to_date(to_char(min(ins_date), 'YYYY-MM-DD'), 'YYYY-MM-DD') as line\n" +
            "                   from (select message, ins_date, row_number() over (order by ins_date) as line\n" +
            "                         from talk_message\n" +
            "                         where talk_id = talk_id)\n" +
            "                   where message like '%' || :search || '%') order by ins_date asc", nativeQuery = true)
    List<ITalkMessageDto> findSearchContent(@Param("talkId") String talkId, @Param("search") String search);

    TalkMessage findTop1ByIdTalkIdOrderByIdInsDateDesc(String talkId);

    @Query(value="select * from (select talk_id,\n" +
            "                      sabun,\n" +
            "                      message,\n" +
            "                      ins_date,\n" +
            "                      row_number() over (partition by talk_id order by ins_date) line\n" +
            "               from talk_message\n" +
            "               where talk_id = :talkId) order by ins_date desc limit 1", nativeQuery = true)
    ITalkMessageDto findLastMessage(@Param("talkId") String talkId);

    @Query(value="select count(*) from talk_message where talk_id = :talkId", nativeQuery = true)
    long findByTalkIdCount(@Param("talkId") String talkId);

    @Query(value="WITH talk_member_filtered AS (\n" +
                "    SELECT talk_id\n" +
                "    FROM talk_member\n" +
                "    WHERE sabun = :sabun AND delete_yn = false\n" +
                ")\n" +
                "SELECT cc.talk_id, cc.sabun, cc.message, cc.ins_date, cc.ins_name, cc.search_yn, cc.line \n" +
                "FROM (select talk_message.*, row_number() over (order by ins_date) line from talk_message) cc\n" +
                "JOIN talk_member_filtered cmf ON cc.talk_id = cmf.talk_id\n" +
                "JOIN talk c ON cc.talk_id = c.talk_id\n" +
                "AND cc.search_yn = true \n" +
                "AND cc.message LIKE '%'|| :search ||'%'\n" +
                "ORDER BY cc.ins_date DESC", nativeQuery = true)
    List<ITalkMessageDto> findAllSearchContent(@Param("search") String search, @Param("sabun") String sabun);


    @Query(value="select count(talk_id) from talk_read where talk_id = :talkId and sabun = :sabun and read_yn = false", nativeQuery = true)
    Integer unreadCountBySabun(@Param("talkId") String talkId, @Param("sabun") String sabun);

    @Query(value="select *\n" +
            "from talk_message\n" +
            "where sabun != :sabun\n" +
            "  and talk_id = :talkId\n" +
            "  and talk_message.ins_date not in\n" +
            "      (select ins_date from talk_read where sabun = :sabun and talk_id = :talkId)", nativeQuery = true)
    List<ITalkMessageDto> unReadContent(@Param("talkId") String talkId, @Param("sabun") String sabun);

    @Query(value="select *\n" +
            "from talk_message\n" +
            "where talk_id = :talkId\n" +
            "  and talk_message.ins_date not in\n" +
            "      (select ins_date from talk_read where talk_id = :talkId)", nativeQuery = true)
    List<ITalkMessageDto> allUnReadContent(@Param("talkId") String talkId);

    @Transactional
    @Modifying
    @Query(value="insert into talk_message\n" +
            "select :talkId, sabun, message, ins_date, ins_name, search_yn from talk_message where talk_id = :pTalkId", nativeQuery = true)
    int dataCopy(@Param("pTalkId") String pTalkId, @Param("talkId") String talkId);

    @Query(value = "WITH SU AS ( SELECT SABUN, WORK_ID, NAME, WORK_NM FROM SM_USER),\n" +
                   "TM AS ( SELECT TALK_ID, SABUN, MESSAGE, INS_DATE, INS_NAME, SEARCH_YN FROM TALK_MESSAGE WHERE 1=1),\n" +
                   "TM2 AS ( SELECT TALK_ID, SABUN, NAME, MARK_YN, DELETE_YN, DUMMY_YN FROM TALK_MEMBER WHERE 1=1),\n" +
                   "M AS ( SELECT NO, KEYWORD FROM MENTAL WHERE 1=1) \n" +
                   "SELECT COUNT(*)\n" +
                   "FROM (\n" +
                   "SELECT DISTINCT SU.SABUN, SU.NAME, TM.MESSAGE, TM.INS_DATE FROM TM\n" +
                   "INNER JOIN TM2 ON TM.TALK_ID = TM2.TALK_ID\n" +
                   "INNER JOIN SU ON TM.SABUN = SU.SABUN\n" +
                   "WHERE SU.SABUN = :sabun\n" +
                   "AND TM.MESSAGE LIKE '%' || :keywordNo || '%'\n" +
                   "AND TM.INS_DATE BETWEEN NOW() - INTERVAL '1' HOUR AND NOW()\n" +
                //    "AND TO_CHAR(TM.INS_DATE, 'YYYYMMDDHH24MISS') <= :date\n" +
                   ") AS MT" ,nativeQuery = true)
    int hoursCountLogic1(@Param("sabun") String sabun, @Param("keywordNo") String keywordNo);

    @Query(value = "WITH SU AS ( SELECT SABUN, WORK_ID, NAME, WORK_NM FROM SM_USER),\n" +
                   "TM AS ( SELECT TALK_ID, SABUN, MESSAGE, INS_DATE, INS_NAME, SEARCH_YN FROM TALK_MESSAGE WHERE 1=1),\n" +
                   "TM2 AS ( SELECT TALK_ID, SABUN, NAME, MARK_YN, DELETE_YN, DUMMY_YN FROM TALK_MEMBER WHERE 1=1),\n" +
                   "M AS ( SELECT NO, KEYWORD FROM MENTAL WHERE 1=1) \n" +
                   "SELECT COUNT(*)\n" +
                   "FROM (\n" +
                   "SELECT DISTINCT SU.SABUN, SU.NAME, TM.MESSAGE, TM.INS_DATE FROM TM\n" +
                   "INNER JOIN TM2 ON TM.TALK_ID = TM2.TALK_ID\n" +
                   "INNER JOIN SU ON TM.SABUN = SU.SABUN\n" +
                   "WHERE SU.SABUN = :sabun\n" +
                   "AND TM.MESSAGE ~ (SELECT KEYWORD FROM M WHERE 1=1 AND M.NO = :keywordNo)\n" +
                   "AND TM.INS_DATE BETWEEN NOW() - INTERVAL '1' HOUR AND NOW()\n" +
                //    "AND TO_CHAR(TM.INS_DATE, 'YYYYMMDDHH24MISS') <= :date\n" +
                   ") AS MT" ,nativeQuery = true)
    int hoursCountLogic2(@Param("sabun") String sabun, @Param("keywordNo") String keywordNo);

    @Query(value = "WITH HOURLY_COUNT AS (\n" +
                   "SELECT COALESCE(ROUND(AVG(OCTET_LENGTH(MESSAGE)),2),0) AS AFTER_SIZE \n" +
                   "FROM TALK_MESSAGE TM \n" +
                   "WHERE 1=1 \n" +
                        "AND SABUN = :sabun\n" +
                        "AND INS_DATE >= DATE_TRUNC('HOUR',NOW()) - INTERVAL '1' HOUR \n" +
                        "AND INS_DATE < DATE_TRUNC('HOUR',NOW())\n" +
                   "),\n" +
                   "PIVOTED AS (\n" +
                   "SELECT COALESCE(ROUND(AVG(OCTET_LENGTH(MESSAGE)),2),0) AS BEFORE_SIZE \n" +
                   "FROM TALK_MESSAGE TM \n" +
                   "WHERE 1=1 \n" +
                        "AND SABUN = :sabun\n" +
                        "AND INS_DATE >= DATE_TRUNC('HOUR',NOW()) - INTERVAL '2' HOUR \n" +
                        "AND INS_DATE < DATE_TRUNC('HOUR',NOW()) - INTERVAL '1' HOUR\n" +
                   ")\n" +
                   "SELECT CASE WHEN BEFORE_SIZE = 0 THEN 0\n" +
                               "ELSE ROUND((BEFORE_SIZE - AFTER_SIZE) / BEFORE_SIZE * 100 ,2) \n" +
                               "END AS DEC_RATE \n" +
                   "FROM HOURLY_COUNT, PIVOTED  \n", nativeQuery = true)
    int hoursCountLogic3(@Param("sabun") String sabun);

    @Modifying
    @Transactional
    @Query(value = "update users set stress = :stress where 1=1 and sabun = :sabun", nativeQuery = true)
    int updateStress(@Param("stress") String stress, @Param("sabun") String sabun);

    @Modifying
    @Transactional
    @Query(value = "update users set stress = 0 ", nativeQuery = true)
    int resetStress();
}