package com.ruoyi.info.collection.mapper;

import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.junit.jupiter.api.Test;

import javax.sql.DataSource;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class CcdiAssetInfoMapperTest {

    private static final String RESOURCE = "mapper/info/collection/CcdiAssetInfoMapper.xml";

    @Test
    void selectByFamilyIdAndPersonId_shouldFilterByOwnershipKey() throws Exception {
        MappedStatement mappedStatement = loadMappedStatement(
                "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectByFamilyIdAndPersonId");

        String sql = renderSql(mappedStatement, Map.of(
                "familyId", "320101199001010011",
                "personId", "320101199201010022"
        ));

        assertTrue(sql.contains("FROM ccdi_asset_info"), sql);
        assertTrue(sql.contains("WHERE family_id = ?"), sql);
        assertTrue(sql.contains("AND person_id = ?"), sql);
    }

    @Test
    void deleteByFamilyIdAndPersonId_shouldFilterByOwnershipKey() throws Exception {
        MappedStatement mappedStatement = loadMappedStatement(
                "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.deleteByFamilyIdAndPersonId");

        String sql = renderSql(mappedStatement, Map.of(
                "familyId", "320101199001010011",
                "personId", "320101199201010022"
        ));

        assertTrue(sql.contains("DELETE FROM ccdi_asset_info"), sql);
        assertTrue(sql.contains("WHERE family_id = ?"), sql);
        assertTrue(sql.contains("AND person_id = ?"), sql);
    }

    @Test
    void insertBatch_shouldIncludeAllBusinessColumns() throws Exception {
        MappedStatement mappedStatement = loadMappedStatement(
                "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.insertBatch");

        CcdiAssetInfo assetInfo = new CcdiAssetInfo();
        assetInfo.setFamilyId("320101199001010011");
        assetInfo.setPersonId("320101199201010022");
        assetInfo.setAssetMainType("车辆");
        assetInfo.setAssetSubType("小汽车");
        assetInfo.setAssetName("家庭车辆");
        assetInfo.setCurrentValue(new BigDecimal("100000.00"));
        assetInfo.setAssetStatus("正常");

        String sql = renderSql(mappedStatement, Map.of("list", List.of(assetInfo)));

        assertTrue(sql.contains("INSERT INTO ccdi_asset_info"), sql);
        assertTrue(sql.contains("family_id"), sql);
        assertTrue(sql.contains("person_id"), sql);
        assertTrue(sql.contains("asset_main_type"), sql);
        assertTrue(sql.contains("current_value"), sql);
        assertTrue(sql.contains("asset_status"), sql);
    }

    @Test
    void ownerLookupQuery_shouldResolveFromFamilyRelationOnly() throws Exception {
        MappedStatement familyStatement = loadMappedStatement(
                "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectOwnerCandidatesByRelationCertNos");

        String familySql = renderSql(familyStatement, Map.of("relationCertNos", List.of("B")));

        assertTrue(familySql.contains("FROM ccdi_staff_fmy_relation"), familySql);
        assertTrue(familySql.contains("relation_cert_no AS personId"), familySql);
        assertTrue(familySql.contains("person_id AS familyId"), familySql);
        assertTrue(familySql.contains("relation_cert_no IN"), familySql);
        assertTrue(familySql.contains("is_emp_family = 1"), familySql);
        assertFalse(familySql.contains("FROM ccdi_base_staff"), familySql);
    }

    private MappedStatement loadMappedStatement(String statementId) throws Exception {
        Configuration configuration = new Configuration();
        configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource()));
        registerTypeAliases(configuration.getTypeAliasRegistry());
        configuration.getLanguageRegistry().register(XMLLanguageDriver.class);
        configuration.addMapper(CcdiAssetInfoMapper.class);

        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(RESOURCE)) {
            XMLMapperBuilder xmlMapperBuilder =
                    new XMLMapperBuilder(inputStream, configuration, RESOURCE, configuration.getSqlFragments());
            xmlMapperBuilder.parse();
        }
        return configuration.getMappedStatement(statementId);
    }

    private String renderSql(MappedStatement mappedStatement, Map<String, Object> params) {
        BoundSql boundSql = mappedStatement.getBoundSql(new HashMap<>(params));
        return boundSql.getSql().replaceAll("\\s+", " ").trim();
    }

    private void registerTypeAliases(TypeAliasRegistry typeAliasRegistry) {
        typeAliasRegistry.registerAlias("map", Map.class);
    }

    private static class NoOpDataSource implements DataSource {

        @Override
        public java.sql.Connection getConnection() {
            throw new UnsupportedOperationException("Not required for SQL rendering tests");
        }

        @Override
        public java.sql.Connection getConnection(String username, String password) {
            throw new UnsupportedOperationException("Not required for SQL rendering tests");
        }

        @Override
        public java.io.PrintWriter getLogWriter() {
            return null;
        }

        @Override
        public void setLogWriter(java.io.PrintWriter out) {
        }

        @Override
        public void setLoginTimeout(int seconds) {
        }

        @Override
        public int getLoginTimeout() {
            return 0;
        }

        @Override
        public java.util.logging.Logger getParentLogger() {
            return java.util.logging.Logger.getGlobal();
        }

        @Override
        public <T> T unwrap(Class<T> iface) {
            throw new UnsupportedOperationException("Not supported");
        }

        @Override
        public boolean isWrapperFor(Class<?> iface) {
            return false;
        }
    }
}
