package server.logic.ws_protocol.JSON.handlers.channels;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
import server.logic.ws_protocol.JSON.handlers.channels.entyties.Net_MarkChannelMessagesSeen_Request;
import server.logic.ws_protocol.JSON.handlers.channels.entyties.Net_MarkChannelMessagesSeen_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.MsgSubType;
import shine.db.SqliteDbController;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Net_MarkChannelMessagesSeen_Handler implements JsonMessageHandler {
    private static final Logger log = LoggerFactory.getLogger(Net_MarkChannelMessagesSeen_Handler.class);

    @Override
    public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
        Net_MarkChannelMessagesSeen_Request req = (Net_MarkChannelMessagesSeen_Request) baseRequest;
        List<Net_MarkChannelMessagesSeen_Request.MessageRef> refs = req.getMessages();
        if (refs == null || refs.isEmpty()) {
            Net_MarkChannelMessagesSeen_Response ok = new Net_MarkChannelMessagesSeen_Response();
            ok.setOp(req.getOp());
            ok.setRequestId(req.getRequestId());
            ok.setStatus(WireCodes.Status.OK);
            ok.setSeenAccepted(0);
            ok.setInserted(0);
            return ok;
        }

        try (Connection c = SqliteDbController.getInstance().getConnection()) {
            String viewerLogin = ctx != null ? ctx.getLogin() : null;
            if (viewerLogin == null || viewerLogin.isBlank()) {
                viewerLogin = ChannelsReadSupport.canonicalLogin(c, req.getLogin());
            }
            if (viewerLogin == null || viewerLogin.isBlank()) {
                return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "bad_fields", "Invalid login");
            }

            Integer expectedRoot = req.getChannel() != null ? req.getChannel().getChannelRootBlockNumber() : null;
            String expectedOwnerBch = req.getChannel() != null ? req.getChannel().getOwnerBlockchainName() : null;
            boolean strictChannelMatch = expectedRoot != null && expectedRoot >= 0
                    && expectedOwnerBch != null && !expectedOwnerBch.isBlank();

            String existsSql = """
                SELECT 1
                FROM blocks
                WHERE bch_name = ?
                  AND block_number = ?
                  AND block_hash = ?
                  AND msg_type = ?
                  AND msg_sub_type = ?
                  %s
                LIMIT 1
                """.formatted(strictChannelMatch ? "AND line_code = ?" : "");

            String insertSql = """
                INSERT OR IGNORE INTO message_views_state (
                    viewer_login, to_bch_name, to_block_number, to_block_hash, first_seen_at_ms
                ) VALUES (?, ?, ?, ?, ?)
                """;

            int seenAccepted = 0;
            int inserted = 0;
            long nowMs = System.currentTimeMillis();
            Set<String> dedup = new HashSet<>();

            try (PreparedStatement existsPs = c.prepareStatement(existsSql);
                 PreparedStatement insertPs = c.prepareStatement(insertSql)) {

                for (Net_MarkChannelMessagesSeen_Request.MessageRef ref : refs) {
                    if (ref == null) continue;
                    String bch = String.valueOf(ref.getBlockchainName() == null ? "" : ref.getBlockchainName()).trim();
                    Integer no = ref.getBlockNumber();
                    String hashHex = String.valueOf(ref.getBlockHash() == null ? "" : ref.getBlockHash()).trim().toLowerCase();

                    if (bch.isBlank() || no == null || no < 0) continue;
                    if (!hashHex.matches("^[0-9a-f]{64}$")) continue;

                    if (strictChannelMatch && !bch.equals(expectedOwnerBch)) continue;

                    String key = bch + "|" + no + "|" + hashHex;
                    if (!dedup.add(key)) continue;

                    existsPs.clearParameters();
                    existsPs.setString(1, bch);
                    existsPs.setInt(2, no);
                    existsPs.setBytes(3, ChannelsReadSupport.hexToBytes(hashHex));
                    existsPs.setInt(4, ChannelsReadSupport.MSG_TYPE_TEXT);
                    existsPs.setInt(5, MsgSubType.TEXT_POST);
                    if (strictChannelMatch) {
                        existsPs.setInt(6, expectedRoot);
                    }

                    boolean exists;
                    try (ResultSet rs = existsPs.executeQuery()) {
                        exists = rs.next();
                    }
                    if (!exists) continue;

                    seenAccepted += 1;

                    insertPs.clearParameters();
                    insertPs.setString(1, viewerLogin);
                    insertPs.setString(2, bch);
                    insertPs.setInt(3, no);
                    insertPs.setBytes(4, ChannelsReadSupport.hexToBytes(hashHex));
                    insertPs.setLong(5, nowMs);
                    inserted += insertPs.executeUpdate();
                }
            }

            Net_MarkChannelMessagesSeen_Response ok = new Net_MarkChannelMessagesSeen_Response();
            ok.setOp(req.getOp());
            ok.setRequestId(req.getRequestId());
            ok.setStatus(WireCodes.Status.OK);
            ok.setSeenAccepted(seenAccepted);
            ok.setInserted(inserted);
            return ok;
        } catch (Exception e) {
            log.error("MarkChannelMessagesSeen failed", e);
            return NetExceptionResponseFactory.error(req, WireCodes.Status.INTERNAL_ERROR, "internal_error", "Internal server error");
        }
    }
}

