package tech.easyflow.chatlog.repository.mysql;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
import tech.easyflow.chatlog.support.ChatConstants;

import java.math.BigInteger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * MySQL 轮次仓储。
 */
@Repository
public class MySqlChatRoundRepository {

    private final JdbcTemplate jdbcTemplate;

    public MySqlChatRoundRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 批量写入或更新轮次聚合。
     *
     * @param commands 轮次命令
     */
    public void createOrTouchBatch(List<ChatRoundUpsertCommand> commands) {
        if (commands == null || commands.isEmpty()) {
            return;
        }
        String sql = "INSERT INTO `" + ChatConstants.ROUND_TABLE + "` " +
                "(id, session_id, round_no, user_message_id, selected_assistant_message_id, selected_variant_index, variant_count, status, created, modified) " +
                "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " +
                "ON DUPLICATE KEY UPDATE user_message_id=COALESCE(VALUES(user_message_id), user_message_id), " +
                "selected_assistant_message_id=CASE " +
                "WHEN VALUES(selected_assistant_message_id) IS NOT NULL AND (selected_assistant_message_id IS NULL OR VALUES(modified) > modified) " +
                "THEN VALUES(selected_assistant_message_id) ELSE selected_assistant_message_id END, " +
                "selected_variant_index=CASE " +
                "WHEN VALUES(selected_variant_index) IS NOT NULL AND (selected_variant_index IS NULL OR selected_variant_index = 0 OR VALUES(modified) > modified) " +
                "THEN VALUES(selected_variant_index) ELSE selected_variant_index END, " +
                "variant_count=GREATEST(COALESCE(VALUES(variant_count), 0), COALESCE(variant_count, 0)), " +
                "status=CASE " +
                "WHEN VALUES(status) IS NULL OR VALUES(status) = '' THEN status " +
                "WHEN VALUES(modified) > modified THEN VALUES(status) " +
                "WHEN VALUES(modified) = modified AND status = '" + ChatConstants.ROUND_STATUS_ANSWERING + "' " +
                "AND VALUES(status) <> '" + ChatConstants.ROUND_STATUS_ANSWERING + "' THEN VALUES(status) " +
                "WHEN VALUES(modified) = modified AND status <> '" + ChatConstants.ROUND_STATUS_LOCKED + "' " +
                "AND VALUES(status) = '" + ChatConstants.ROUND_STATUS_LOCKED + "' THEN VALUES(status) " +
                "ELSE status END, " +
                "modified=GREATEST(VALUES(modified), modified)";
        jdbcTemplate.batchUpdate(sql, commands, commands.size(), (ps, command) -> {
            Timestamp operateAt = timestamp(command.getOperateAt());
            ps.setObject(1, command.getRoundId());
            ps.setObject(2, command.getSessionId());
            ps.setObject(3, command.getRoundNo());
            ps.setObject(4, command.getUserMessageId());
            ps.setObject(5, command.getSelectedAssistantMessageId());
            ps.setObject(6, command.getSelectedVariantIndex());
            ps.setObject(7, command.getVariantCount());
            ps.setString(8, command.getStatus());
            ps.setTimestamp(9, operateAt);
            ps.setTimestamp(10, operateAt);
        });
    }

    /**
     * 应用答案版本切换。
     *
     * @param commands 切换命令
     */
    public void selectVariants(List<ChatRoundSelectCommand> commands) {
        if (commands == null || commands.isEmpty()) {
            return;
        }
        String sql = "UPDATE `" + ChatConstants.ROUND_TABLE + "` SET selected_assistant_message_id=?, selected_variant_index=?, modified=? " +
                "WHERE id=? AND session_id=? AND modified <= ?";
        jdbcTemplate.batchUpdate(sql, commands, commands.size(), (ps, command) -> {
            Timestamp operateAt = timestamp(command.getOperateAt());
            ps.setObject(1, command.getSelectedAssistantMessageId());
            ps.setObject(2, command.getSelectedVariantIndex());
            ps.setTimestamp(3, operateAt);
            ps.setObject(4, command.getRoundId());
            ps.setObject(5, command.getSessionId());
            ps.setTimestamp(6, operateAt);
        });
    }

    /**
     * 查询指定轮次。
     *
     * @param sessionId 会话 ID
     * @param roundId 轮次 ID
     * @return 轮次记录
     */
    public ChatRoundRecord findRound(BigInteger sessionId, BigInteger roundId) {
        List<ChatRoundRecord> records = jdbcTemplate.query(
                "SELECT * FROM `" + ChatConstants.ROUND_TABLE + "` WHERE session_id=? AND id=? LIMIT 1",
                rowMapper(),
                sessionId,
                roundId
        );
        return records.isEmpty() ? null : records.get(0);
    }

    /**
     * 查询会话最新轮次。
     *
     * @param sessionId 会话 ID
     * @return 最新轮次
     */
    public ChatRoundRecord findLatestRound(BigInteger sessionId) {
        List<ChatRoundRecord> records = jdbcTemplate.query(
                "SELECT * FROM `" + ChatConstants.ROUND_TABLE + "` WHERE session_id=? ORDER BY round_no DESC, id DESC LIMIT 1",
                rowMapper(),
                sessionId
        );
        return records.isEmpty() ? null : records.get(0);
    }

    /**
     * 判断会话是否已使用轮次模型。
     *
     * @param sessionId 会话 ID
     * @return 是否存在轮次
     */
    public boolean existsRounds(BigInteger sessionId) {
        Long count = jdbcTemplate.queryForObject(
                "SELECT COUNT(1) FROM `" + ChatConstants.ROUND_TABLE + "` WHERE session_id=?",
                Long.class,
                sessionId
        );
        return count != null && count > 0;
    }

    /**
     * 读取指定时间之后变更的轮次。
     *
     * @param cursorTime 游标时间
     * @param cursorId 游标 ID
     * @param limit 批大小
     * @return 轮次记录列表
     */
    public List<ChatRoundRecord> loadModifiedAfter(Date cursorTime, BigInteger cursorId, int limit) {
        Timestamp timestamp = cursorTime == null ? new Timestamp(0L) : new Timestamp(cursorTime.getTime());
        return jdbcTemplate.query(
                "SELECT * FROM `" + ChatConstants.ROUND_TABLE + "` WHERE (modified > ?) OR (modified = ? AND id > ?) " +
                        "ORDER BY modified ASC, id ASC LIMIT ?",
                rowMapper(),
                timestamp,
                timestamp,
                cursorId == null ? BigInteger.ZERO : cursorId,
                limit
        );
    }

    private RowMapper<ChatRoundRecord> rowMapper() {
        return new RowMapper<>() {
            @Override
            public ChatRoundRecord mapRow(ResultSet rs, int rowNum) throws SQLException {
                ChatRoundRecord record = new ChatRoundRecord();
                record.setId(bigInteger(rs, "id"));
                record.setSessionId(bigInteger(rs, "session_id"));
                record.setRoundNo(rs.getInt("round_no"));
                record.setUserMessageId(bigInteger(rs, "user_message_id"));
                record.setSelectedAssistantMessageId(bigInteger(rs, "selected_assistant_message_id"));
                record.setSelectedVariantIndex(rs.getInt("selected_variant_index"));
                record.setVariantCount(rs.getInt("variant_count"));
                record.setStatus(rs.getString("status"));
                record.setCreated(rs.getTimestamp("created"));
                record.setModified(rs.getTimestamp("modified"));
                return record;
            }
        };
    }

    private BigInteger bigInteger(ResultSet rs, String column) throws SQLException {
        Object value = rs.getObject(column);
        if (value == null) {
            return null;
        }
        return new BigInteger(String.valueOf(value));
    }

    private Timestamp timestamp(Date value) {
        return new Timestamp((value == null ? new Date() : value).getTime());
    }
}
