package com.njcn.gather.tool.adddata.component;

import com.njcn.gather.tool.adddata.pojo.bo.AddDataBatchWriteResult;
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 批量写入组件。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AddDataBatchWriter {

    /** JDBC 模板。 */
    private final JdbcTemplate jdbcTemplate;

    /**
     * 写入一个批次。
     *
     * @param definition 表定义
     * @param rows 行数据
     * @return 写入结果
     */
    public AddDataBatchWriteResult writeBatch(AddDataTableDefinition definition, List<List<Object>> rows) {
        if (rows == null || rows.isEmpty()) {
            return new AddDataBatchWriteResult(0L, 0L, 0L, null);
        }
        try {
            return executeInsertIgnore(definition, rows);
        } catch (DataAccessException ex) {
            log.warn("批量写入失败，开始降级为逐行写入，table={}, batchSize={}, message={}",
                    definition.getTableName(), rows.size(), resolveErrorMessage(ex));
            return fallbackWriteOneByOne(definition, rows, ex);
        }
    }

    /**
     * 执行批量 INSERT IGNORE。
     *
     * @param definition 表定义
     * @param rows 行数据
     * @return 写入结果
     */
    private AddDataBatchWriteResult executeInsertIgnore(AddDataTableDefinition definition, List<List<Object>> rows) {
        String sql = buildInsertIgnoreSql(definition, rows.size());
        List<Object> args = flattenRows(rows);
        int affectedRows = jdbcTemplate.update(sql, args.toArray(new Object[0]));
        long insertedCount = Math.max(affectedRows, 0);
        long skippedCount = rows.size() - insertedCount;
        return new AddDataBatchWriteResult(insertedCount, skippedCount, 0L, null);
    }

    /**
     * 批量写入失败后逐行降级。
     *
     * @param definition 表定义
     * @param rows 行数据
     * @param batchException 原始批量异常
     * @return 写入结果
     */
    private AddDataBatchWriteResult fallbackWriteOneByOne(AddDataTableDefinition definition, List<List<Object>> rows, DataAccessException batchException) {
        long insertedCount = 0L;
        long skippedCount = 0L;
        long failedCount = 0L;
        String firstFailureMessage = null;
        String sql = buildInsertIgnoreSql(definition, 1);
        for (List<Object> row : rows) {
            try {
                int affectedRows = jdbcTemplate.update(sql, row.toArray(new Object[0]));
                if (affectedRows > 0) {
                    insertedCount += affectedRows;
                } else {
                    skippedCount++;
                }
            } catch (DataAccessException ex) {
                failedCount++;
                if (firstFailureMessage == null) {
                    firstFailureMessage = resolveErrorMessage(ex);
                }
            }
        }
        if (firstFailureMessage == null) {
            firstFailureMessage = resolveErrorMessage(batchException);
        }
        return new AddDataBatchWriteResult(insertedCount, skippedCount, failedCount, firstFailureMessage);
    }

    /**
     * 构建 INSERT IGNORE SQL。
     *
     * @param definition 表定义
     * @param rowCount 行数
     * @return SQL 文本
     */
    private String buildInsertIgnoreSql(AddDataTableDefinition definition, int rowCount) {
        String columnSegment = "`" + String.join("`,`", definition.getColumns()) + "`";
        String placeholderSegment = "(" + String.join(",", Collections.nCopies(definition.getColumns().size(), "?")) + ")";
        String valuesSegment = String.join(",", Collections.nCopies(rowCount, placeholderSegment));
        return "INSERT IGNORE INTO `" + definition.getTableName() + "` (" + columnSegment + ") VALUES " + valuesSegment;
    }

    /**
     * 展平批量参数。
     *
     * @param rows 行数据
     * @return 扁平参数列表
     */
    private List<Object> flattenRows(List<List<Object>> rows) {
        List<Object> args = new ArrayList<Object>();
        for (List<Object> row : rows) {
            args.addAll(row);
        }
        return args;
    }

    /**
     * 提取错误信息。
     *
     * @param ex 异常
     * @return 错误信息
     */
    private String resolveErrorMessage(DataAccessException ex) {
        Throwable root = ex.getMostSpecificCause();
        return root == null ? ex.getMessage() : root.getMessage();
    }
}
