package tech.easyflow.datacenter.execution.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import org.springframework.stereotype.Service;
import tech.easyflow.common.cache.RedisIdempotencyExecutor;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.datacenter.connector.DatacenterConnector;
import tech.easyflow.datacenter.connector.DatacenterConnectorRegistry;
import tech.easyflow.datacenter.entity.DatacenterTable;
import tech.easyflow.datacenter.execution.model.DatasetRef;
import tech.easyflow.datacenter.meta.entity.DatacenterSource;
import tech.easyflow.datacenter.meta.service.DatacenterDatasetRegistryService;
import tech.easyflow.datacenter.execution.service.DatacenterDatasetWriteService;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import java.util.List;

@Service
public class DatacenterDatasetWriteServiceImpl implements DatacenterDatasetWriteService {

    @Resource
    private DatacenterDatasetRegistryService registryService;
    @Resource
    private DatacenterConnectorRegistry connectorRegistry;
    @Resource
    private RedisIdempotencyExecutor idempotencyExecutor;

    /**
     * {@inheritDoc}
     */
    @Override
    public void saveRow(DatasetRef datasetRef, JSONObject data, LoginAccount account) {
        DatacenterTable table = resolveTable(datasetRef);
        DatacenterSource source = registryService.getSourceRequired(table.getSourceId());
        DatacenterConnector connector = connectorRegistry.getConnector(source.getSourceType());
        connector.saveRow(source, table, data, account);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void saveRows(DatasetRef datasetRef, List<JSONObject> rows, LoginAccount account, int batchSize) {
        if (rows == null || rows.isEmpty()) {
            return;
        }
        DatacenterTable table = resolveTable(datasetRef);
        DatacenterSource source = registryService.getSourceRequired(table.getSourceId());
        DatacenterConnector connector = connectorRegistry.getConnector(source.getSourceType());
        connector.saveRows(source, table, rows, account, Math.max(1, batchSize));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean saveRowsIdempotently(DatasetRef datasetRef,
                                        List<JSONObject> rows,
                                        LoginAccount account,
                                        int batchSize,
                                        String idempotencyKey) {
        if (rows == null || rows.isEmpty()) {
            return true;
        }
        DatacenterTable table = resolveTable(datasetRef);
        DatacenterSource source = registryService.getSourceRequired(table.getSourceId());
        DatacenterConnector connector = connectorRegistry.getConnector(source.getSourceType());
        String payloadHash = sha256Rows(rows);
        if (idempotencyKey == null || idempotencyKey.isBlank()) {
            connector.saveRows(source, table, rows, account, Math.max(1, batchSize));
            return true;
        }
        String receiptKey = sha256(idempotencyKey);
        return idempotencyExecutor.executeOnce(idempotencyKey, payloadHash, () ->
                connector.saveRowsIdempotently(
                        source,
                        table,
                        rows,
                        account,
                        Math.max(1, batchSize),
                        receiptKey,
                        payloadHash));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteRow(DatasetRef datasetRef, BigInteger id, LoginAccount account) {
        DatacenterTable table = resolveTable(datasetRef);
        DatacenterSource source = registryService.getSourceRequired(table.getSourceId());
        DatacenterConnector connector = connectorRegistry.getConnector(source.getSourceType());
        connector.deleteRow(source, table, id, account);
    }

    private DatacenterTable resolveTable(DatasetRef datasetRef) {
        if (datasetRef == null || datasetRef.getTableId() == null) {
            throw new BusinessException("缺少 tableId");
        }
        return registryService.getTableWithFields(datasetRef.getTableId());
    }

    /**
     * 计算稳定的 SHA-256 摘要。
     *
     * @param value 原始文本
     * @return 十六进制摘要
     */
    private String sha256(String value) {
        try {
            return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256")
                    .digest(value.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException error) {
            throw new IllegalStateException("SHA-256 is unavailable", error);
        }
    }

    /**
     * 逐行计算与排序字段 JSON 数组等价的 SHA-256，避免构造整批字符串副本。
     *
     * @param rows 待写入行
     * @return 十六进制摘要
     */
    private String sha256Rows(List<JSONObject> rows) {
        try {
            MessageDigest digest =
                    MessageDigest.getInstance("SHA-256");
            digest.update((byte) '[');
            for (int index = 0; index < rows.size(); index++) {
                if (index > 0) {
                    digest.update((byte) ',');
                }
                digest.update(JSON.toJSONBytes(
                        rows.get(index),
                        JSONWriter.Feature.MapSortField));
            }
            digest.update((byte) ']');
            return HexFormat.of().formatHex(digest.digest());
        } catch (NoSuchAlgorithmException error) {
            throw new IllegalStateException(
                    "SHA-256 is unavailable", error);
        }
    }
}
