package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;

import server.logic.ws_protocol.JSON.entyties.Net_Request;

/**
 * Запрос AddUser — временная/тестовая регистрация локального пользователя.
 *
 * Клиент отправляет:
 *
 * {
 *   "op": "AddUser",
 *   "requestId": "test-add-1",
 *   "payload": {
 *     "login": "anya",
 *     "blockchainName": "anya-001",
 *     "solanaKey": "base64-ed25519-public-key-login",
 *     "blockchainKey": "base64-ed25519-public-key-blockchain",
 *     "deviceKey": "base64-ed25519-public-key-device",
 *     "bchLimit": 1000000
 *   }
 * }
 *
 * Все поля лежат внутри payload.
 */
public class Net_AddUser_Request extends Net_Request {

    private String login;
    private String blockchainName;

    /** Ключ пользователя Solana (публичный ключ логина) */
    private String solanaKey;

    /** Ключ блокчейна (публичный ключ блокчейна) */
    private String blockchainKey;

    /** Ключ устройства (публичный ключ устройства) */
    private String deviceKey;

    private Integer bchLimit;

    public String getLogin() { return login; }
    public void setLogin(String login) { this.login = login; }

    public String getBlockchainName() { return blockchainName; }
    public void setBlockchainName(String blockchainName) { this.blockchainName = blockchainName; }

    public String getSolanaKey() { return solanaKey; }
    public void setSolanaKey(String solanaKey) { this.solanaKey = solanaKey; }

    public String getBlockchainKey() { return blockchainKey; }
    public void setBlockchainKey(String blockchainKey) { this.blockchainKey = blockchainKey; }

    public String getDeviceKey() { return deviceKey; }
    public void setDeviceKey(String deviceKey) { this.deviceKey = deviceKey; }

    public Integer getBchLimit() { return bchLimit; }
    public void setBchLimit(Integer bchLimit) { this.bchLimit = bchLimit; }
}
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;

import server.logic.ws_protocol.JSON.entyties.Net_Response;

/**
 * Успешный ответ на AddUser.
 *
 * Сейчас дополнительных полей нет — достаточно status=200.
 *
 * Пример:
 * {
 *   "op": "AddUser",
 *   "requestId": "test-add-1",
 *   "status": 200,
 *   "payload": { }
 * }
 */
public class Net_AddUser_Response extends Net_Response {
    // При необходимости сюда можно добавить, например, флаг created/updated и т.п.
}
package server.logic.ws_protocol.JSON.handlers.tempToTest;

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.tempToTest.entyties.Net_AddUser_Request;
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.SqliteDbController;
import shine.db.dao.BlockchainStateDAO;
import shine.db.dao.SolanaUsersDAO;
import shine.db.entities.BlockchainStateEntry;
import shine.db.entities.SolanaUserEntry;
import utils.blockchain.BlockchainNameUtil;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Base64;

public class Net_AddUser_Handler implements JsonMessageHandler {

    private static final Logger log = LoggerFactory.getLogger(Net_AddUser_Handler.class);

    /** TEST ONLY */
    private static final int TEST_BCH_LIMIT = 1_000_000;

    @Override
    public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
        Net_AddUser_Request req = (Net_AddUser_Request) baseRequest;

        if (req.getLogin() == null || req.getLogin().isBlank()
                || req.getBlockchainName() == null || req.getBlockchainName().isBlank()
                || req.getSolanaKey() == null || req.getSolanaKey().isBlank()
                || req.getBlockchainKey() == null || req.getBlockchainKey().isBlank()
                || req.getDeviceKey() == null || req.getDeviceKey().isBlank()) {

            return NetExceptionResponseFactory.error(
                    req,
                    WireCodes.Status.BAD_REQUEST,
                    "BAD_FIELDS",
                    "Некорректные поля: login/blockchainName/solanaKey/blockchainKey/deviceKey"
            );
        }

        // blockchainName должен быть вида: <login>-NNN
        if (!BlockchainNameUtil.isBlockchainNameMatchesLogin(req.getBlockchainName(), req.getLogin())) {
            return NetExceptionResponseFactory.error(
                    req,
                    WireCodes.Status.BAD_REQUEST,
                    "BAD_BLOCKCHAIN_NAME",
                    "blockchainName должен быть вида <login>-NNN (пример: anya-001)"
            );
        }

        int limit = (req.getBchLimit() == null || req.getBchLimit() <= 0)
                ? TEST_BCH_LIMIT
                : req.getBchLimit();

        try {
            byte[] blockchainKey32 = Base64.getDecoder().decode(req.getBlockchainKey());
            if (blockchainKey32.length != 32) {
                return NetExceptionResponseFactory.error(
                        req,
                        WireCodes.Status.BAD_REQUEST,
                        "BAD_BLOCKCHAIN_KEY",
                        "blockchainKey должен быть Base64(32 bytes)"
                );
            }

            SolanaUsersDAO usersDAO = SolanaUsersDAO.getInstance();
            BlockchainStateDAO stateDAO = BlockchainStateDAO.getInstance();

            SqliteDbController db = SqliteDbController.getInstance();

            try (Connection c = db.getConnection()) {
                c.setAutoCommit(false);

                // 1. Проверяем, что пользователя нет
                if (usersDAO.getByLogin(req.getLogin()) != null) {
                    return NetExceptionResponseFactory.error(
                            req,
                            409,
                            "USER_ALREADY_EXISTS",
                            "Пользователь с таким login уже существует"
                    );
                }

                // 2. Проверяем, что blockchain_state ещё нет
                if (stateDAO.getByBlockchainName(req.getBlockchainName()) != null) {
                    return NetExceptionResponseFactory.error(
                            req,
                            409,
                            "BLOCKCHAIN_ALREADY_EXISTS",
                            "blockchain_state уже существует"
                    );
                }

                // 3. Создаём пользователя (solanaKey + deviceKey)
                SolanaUserEntry user = new SolanaUserEntry(
                        req.getLogin(),
                        req.getSolanaKey(),
                        req.getDeviceKey()
                );

                usersDAO.insert(c, user);

                // 4. Создаём INITIAL blockchain_state (blockchainKey)
                BlockchainStateEntry st = new BlockchainStateEntry();
                st.setBlockchainName(req.getBlockchainName());
                st.setLogin(req.getLogin());
                st.setBlockchainKey(req.getBlockchainKey()); // Base64(32)
                st.setLastBlockNumber(-1);
                st.setLastBlockHash(new byte[32]);
                st.setFileSizeBytes(0);
                st.setSizeLimit(limit);
                st.setUpdatedAtMs(System.currentTimeMillis());

                stateDAO.upsert(c, st);

                c.commit();
            }

            Net_AddUser_Response resp = new Net_AddUser_Response();
            resp.setOp(req.getOp());
            resp.setRequestId(req.getRequestId());
            resp.setStatus(WireCodes.Status.OK);

            log.info("✅ AddUser ok: login={}, blockchainName={}, limit={}",
                    req.getLogin(), req.getBlockchainName(), limit);

            return resp;

        } catch (IllegalArgumentException e) {
            return NetExceptionResponseFactory.error(
                    req,
                    WireCodes.Status.BAD_REQUEST,
                    "BAD_KEY_FORMAT",
                    e.getMessage()
            );
        } catch (SQLException e) {
            log.error("❌ DB error AddUser", e);
            return NetExceptionResponseFactory.error(
                    req,
                    WireCodes.Status.SERVER_DATA_ERROR,
                    "DB_ERROR",
                    "Ошибка БД"
            );
        } catch (Exception e) {
            log.error("❌ Internal error AddUser", e);
            return NetExceptionResponseFactory.error(
                    req,
                    WireCodes.Status.INTERNAL_ERROR,
                    "INTERNAL_ERROR",
                    "Внутренняя ошибка сервера"
            );
        }
    }
}
