package com.huashe.park.infrastructure.cfg.mybatis;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import com.huashe.common.annotation.mybatis.Tenant;
import com.huashe.park.common.animations.mybatis.MethodMetadataCache;

import cn.hutool.core.lang.Pair;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SubSelect;

@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {
        Connection.class, Integer.class
    })
})
@Component
public class TenantSqlInterceptor extends BaseInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(handler);

        // 获取原始 SQL 和 MappedStatement
        String originalSql = (String) metaObject.getValue("delegate.boundSql.sql");
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        Pair<Method, Boolean> mapperMethod = getMapperMethod(mappedStatement);
        // 判断是否需要添加租户条件
        if (!ObjectUtils.allNotNull(mapperMethod.getKey(), getLoginUser().getUser().getTenantId())) {
            return invocation.proceed();
        }
        // 解析 SQL 语法树
        Statement stmt = CCJSqlParserUtil.parse(originalSql);
        if (!(stmt instanceof Select)) {
            return invocation.proceed();
        }
        Select select = (Select) stmt;
        SelectBody selectBody = select.getSelectBody();
        if (!(selectBody instanceof PlainSelect)) {
            return invocation.proceed();
        }
        PlainSelect plainSelect = (PlainSelect) selectBody;
        // 构建租户条件表达式（tenant_id = ?）
        EqualsTo tenantCondition = new EqualsTo();
        tenantCondition.setLeftExpression(handleColumn(mapperMethod.getKey()));
        tenantCondition.setRightExpression(new LongValue(getLoginUser().getUser().getTenantId()));
        // *_COUNT 结尾的查询方法且from组合查询子句添加租户条件
        // SELECT count(0) FROM (SELECT * FROM flow_task AS t LEFT JOIN flow_user uu ON uu.associated = t.id)
        // table_count
        if (mapperMethod.getValue() && plainSelect.getFromItem() instanceof SubSelect) {
            SelectBody subSelect = ((SubSelect) plainSelect.getFromItem()).getSelectBody();
            PlainSelect subPlainSelect = (PlainSelect) subSelect;
            // 添加到原有WHERE条件
            if (subPlainSelect.getWhere() != null) {
                subPlainSelect.setWhere(new AndExpression(subPlainSelect.getWhere(), tenantCondition));
            }
            else {
                subPlainSelect.setWhere(tenantCondition);
            }
            // 生成修改后的 SQL
            String modifiedSql = plainSelect.toString();
            metaObject.setValue("delegate.boundSql.sql", modifiedSql);

            return invocation.proceed();
        }

        // 修改 WHERE 条件
        Expression where = plainSelect.getWhere();
        if (where == null) {
            // 没有 WHERE 子句时直接添加
            plainSelect.setWhere(tenantCondition);
        }
        else {
            // 已有 WHERE 子句时用 AND 连接
            AndExpression and = new AndExpression(where, tenantCondition);
            plainSelect.setWhere(and);
        }

        // 生成修改后的 SQL
        String modifiedSql = plainSelect.toString();
        metaObject.setValue("delegate.boundSql.sql", modifiedSql);

        return invocation.proceed();
    }

    private Pair<Method, Boolean> getMapperMethod(MappedStatement mappedStatement) {
        String methodId = mappedStatement.getId();
        int lastDotIndex = methodId.lastIndexOf(".");
        String methodName = methodId.substring(lastDotIndex + 1);
        List<Method> annotatedMethods = MethodMetadataCache.getAnnotatedMethods(mappedStatement);
        boolean endWithCount = false;
        if (methodName.endsWith("_COUNT")) {
            methodName = methodName.replaceFirst("_COUNT", "");
            endWithCount = true;
        }
        String finalMethodName = methodName;
        return Pair.of(annotatedMethods.stream().filter(method -> method.getName().equals(finalMethodName)).findFirst()
            .orElse(null), endWithCount);
    }

    private Column handleColumn(Method mapperMethod) {
        Tenant annotation = mapperMethod.getAnnotation(Tenant.class);
        if (!ObjectUtils.isNotEmpty(annotation)) {
            return new Column("tenant_id");
        }
        if (ObjectUtils.isNotEmpty(annotation.tableAlias())) {
            return new Column(String.format("%s.%s", annotation.tableAlias(), annotation.field()));
        }
        return new Column(annotation.field());
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
