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_GetGroupDialog_Request;
import server.logic.ws_protocol.JSON.handlers.channels.entyties.Net_GetGroupDialog_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.SqliteDbController;
import shine.db.channels.ChannelNameRules;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

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

    @Override
    public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
        Net_GetGroupDialog_Request req = (Net_GetGroupDialog_Request) baseRequest;
        if (req.getGroup() == null
                || req.getGroup().getOwnerBlockchainName() == null
                || req.getGroup().getOwnerBlockchainName().isBlank()
                || req.getGroup().getChannelRootBlockNumber() == null) {
            return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "bad_fields", "Некорректные поля group");
        }

        try (Connection c = SqliteDbController.getInstance().getConnection()) {
            Net_GetGroupDialog_Response resp = new Net_GetGroupDialog_Response();
            resp.setOp(req.getOp());
            resp.setRequestId(req.getRequestId());
            resp.setStatus(WireCodes.Status.OK);

            String ownerBch = req.getGroup().getOwnerBlockchainName().trim();
            int root = req.getGroup().getChannelRootBlockNumber();
            fillGroupInfo(c, ownerBch, root, resp);

            List<MsgRow> all = new ArrayList<>();
            all.addAll(loadTextByLine(c, ownerBch, root));
            for (MemberRef ref : loadActiveMembers(c, ownerBch, root)) {
                GroupChannelRef target = resolveMemberChannel(c, ref);
                if (target == null) continue;
                all.addAll(loadTextByLine(c, target.ownerBch, target.rootNumber));
            }
            all.sort(Comparator.comparingLong(o -> o.createdAtMs));
            resp.setMessages(toOut(all));
            return resp;
        } catch (Exception e) {
            log.error("GetGroupDialog failed", e);
            return NetExceptionResponseFactory.error(req, WireCodes.Status.INTERNAL_ERROR, "internal_error", "Внутренняя ошибка сервера");
        }
    }

    private void fillGroupInfo(Connection c, String ownerBch, int root, Net_GetGroupDialog_Response resp) throws Exception {
        String sql = """
            SELECT owner_login, owner_bch_name, channel_root_block_number, channel_name, chat_title
            FROM chat200_state
            WHERE owner_bch_name = ? AND channel_root_block_number = ?
            LIMIT 1
            """;
        Net_GetGroupDialog_Response.GroupInfo g = new Net_GetGroupDialog_Response.GroupInfo();
        try (PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, ownerBch);
            ps.setInt(2, root);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    g.setOwnerLogin(rs.getString("owner_login"));
                    g.setOwnerBlockchainName(rs.getString("owner_bch_name"));
                    g.setChannelRootBlockNumber(rs.getInt("channel_root_block_number"));
                    g.setChannelName(rs.getString("channel_name"));
                    g.setChatTitle(rs.getString("chat_title"));
                    resp.setGroup(g);
                    return;
                }
            }
        }
        g.setOwnerBlockchainName(ownerBch);
        g.setChannelRootBlockNumber(root);
        g.setChannelName("");
        g.setChatTitle("");
        resp.setGroup(g);
    }

    private List<MemberRef> loadActiveMembers(Connection c, String ownerBch, int root) throws Exception {
        String sql = """
            SELECT member_login, member_channel_name
            FROM chat200_members_state
            WHERE owner_bch_name = ? AND channel_root_block_number = ? AND is_active = 1
            """;
        List<MemberRef> out = new ArrayList<>();
        try (PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, ownerBch);
            ps.setInt(2, root);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    MemberRef ref = new MemberRef();
                    ref.memberLogin = rs.getString("member_login");
                    ref.memberChannelName = rs.getString("member_channel_name");
                    out.add(ref);
                }
            }
        }
        return out;
    }

    private GroupChannelRef resolveMemberChannel(Connection c, MemberRef ref) throws Exception {
        String canonicalLogin = ChannelsReadSupport.canonicalLogin(c, ref.memberLogin);
        if (canonicalLogin == null || canonicalLogin.isBlank()) return null;

        String bchSql = "SELECT blockchain_name FROM blockchain_state WHERE login = ? COLLATE NOCASE ORDER BY blockchain_name LIMIT 1";
        String memberBch = null;
        try (PreparedStatement ps = c.prepareStatement(bchSql)) {
            ps.setString(1, canonicalLogin);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) memberBch = rs.getString("blockchain_name");
            }
        }
        if (memberBch == null || memberBch.isBlank()) return null;

        String slug;
        try {
            slug = ChannelNameRules.toCanonicalSlug(ref.memberChannelName);
        } catch (Exception e) {
            return null;
        }

        String rootSql = """
            SELECT channel_root_block_number
            FROM channel_names_state
            WHERE owner_bch_name = ? AND channel_type_code = 200 AND slug = ?
            ORDER BY channel_root_block_number DESC
            LIMIT 1
            """;
        try (PreparedStatement ps = c.prepareStatement(rootSql)) {
            ps.setString(1, memberBch);
            ps.setString(2, slug);
            try (ResultSet rs = ps.executeQuery()) {
                if (!rs.next()) return null;
                GroupChannelRef out = new GroupChannelRef();
                out.ownerBch = memberBch;
                out.rootNumber = rs.getInt("channel_root_block_number");
                return out;
            }
        }
    }

    private List<MsgRow> loadTextByLine(Connection c, String ownerBch, int line) throws Exception {
        String sql = """
            SELECT login, bch_name, block_number, block_hash, block_bytes
            FROM blocks
            WHERE bch_name = ? AND msg_type = 1 AND line_code = ?
            ORDER BY block_number ASC
            """;
        List<MsgRow> out = new ArrayList<>();
        try (PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, ownerBch);
            ps.setInt(2, line);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    MsgRow row = new MsgRow();
                    row.authorLogin = rs.getString("login");
                    row.authorBch = rs.getString("bch_name");
                    row.blockNumber = rs.getInt("block_number");
                    row.blockHash = rs.getBytes("block_hash");
                    ChannelsReadSupport.TextInfo ti = ChannelsReadSupport.parseTextAndTime(rs.getBytes("block_bytes"));
                    row.createdAtMs = ti.createdAtMs;
                    row.text = ti.text;
                    out.add(row);
                }
            }
        }
        return out;
    }

    private List<Net_GetGroupDialog_Response.MessageItem> toOut(List<MsgRow> rows) {
        List<Net_GetGroupDialog_Response.MessageItem> out = new ArrayList<>();
        for (MsgRow r : rows) {
            Net_GetGroupDialog_Response.MessageItem item = new Net_GetGroupDialog_Response.MessageItem();
            item.setAuthorLogin(r.authorLogin);
            item.setAuthorBlockchainName(r.authorBch);
            item.setBlockNumber(r.blockNumber);
            item.setBlockHash(ChannelsReadSupport.toHex(r.blockHash));
            item.setCreatedAtMs(r.createdAtMs);
            item.setText(r.text);
            out.add(item);
        }
        return out;
    }

    private static final class MemberRef {
        String memberLogin;
        String memberChannelName;
    }

    private static final class GroupChannelRef {
        String ownerBch;
        int rootNumber;
    }

    private static final class MsgRow {
        String authorLogin;
        String authorBch;
        int blockNumber;
        byte[] blockHash;
        long createdAtMs;
        String text;
    }
}

