package cn.fateverse.query.submeter.result;

import cn.fateverse.query.submeter.adapter.WrapperAdapter;
import org.apache.shardingsphere.sql.parser.binder.segment.select.projection.Projection;
import org.apache.shardingsphere.sql.parser.binder.segment.select.projection.impl.ColumnProjection;
import org.apache.shardingsphere.sql.parser.binder.statement.SQLStatementContext;
import org.apache.shardingsphere.sql.parser.binder.statement.dml.SelectStatementContext;
import org.apache.shardingsphere.underlying.common.database.DefaultSchema;

import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;

/**
 * @author Clay
 * @date 2024/4/8 14:04
 */
public class SubmeterResultSetMetaData extends WrapperAdapter implements ResultSetMetaData {

    private final ResultSetMetaData resultSetMetaData;

    private final SQLStatementContext sqlStatementContext;

    public SubmeterResultSetMetaData(ResultSetMetaData resultSetMetaData, SQLStatementContext sqlStatementContext) {
        this.resultSetMetaData = resultSetMetaData;
        this.sqlStatementContext = sqlStatementContext;
    }


    @Override
    public int getColumnCount() throws SQLException {
        return sqlStatementContext instanceof SelectStatementContext ? ((SelectStatementContext) sqlStatementContext).getProjectionsContext().getExpandProjections().size() : 0;
    }


    @Override
    public boolean isAutoIncrement(final int column) throws SQLException {
        return resultSetMetaData.isAutoIncrement(column);
    }

    @Override
    public boolean isCaseSensitive(final int column) throws SQLException {
        return resultSetMetaData.isCaseSensitive(column);
    }

    @Override
    public boolean isSearchable(final int column) throws SQLException {
        return resultSetMetaData.isSearchable(column);
    }

    @Override
    public boolean isCurrency(final int column) throws SQLException {
        return resultSetMetaData.isCurrency(column);
    }

    @Override
    public int isNullable(final int column) throws SQLException {
        return resultSetMetaData.isNullable(column);
    }

    @Override
    public boolean isSigned(final int column) throws SQLException {
        return resultSetMetaData.isSigned(column);
    }

    @Override
    public int getColumnDisplaySize(final int column) throws SQLException {
        return resultSetMetaData.getColumnDisplaySize(column);
    }

    @Override
    public String getColumnLabel(final int column) throws SQLException {
        return resultSetMetaData.getColumnLabel(column);
    }

    @Override
    public String getColumnName(final int column) throws SQLException {
        if (sqlStatementContext instanceof SelectStatementContext) {
            List<Projection> actualProjections = ((SelectStatementContext) sqlStatementContext).getProjectionsContext().getExpandProjections();
            if (column > actualProjections.size()) {
                throw new SQLException("Column index out of range.", "S1002", 0);
            }
            Projection projection = ((SelectStatementContext) sqlStatementContext).getProjectionsContext().getExpandProjections().get(column - 1);
            if (projection instanceof ColumnProjection) {
                return ((ColumnProjection) projection).getName();
            }
        }
        return resultSetMetaData.getColumnName(column);
    }

    @Override
    public String getSchemaName(final int column) {
        return DefaultSchema.LOGIC_NAME;
    }

    @Override
    public int getPrecision(final int column) throws SQLException {
        return resultSetMetaData.getPrecision(column);
    }

    @Override
    public int getScale(final int column) throws SQLException {
        return resultSetMetaData.getScale(column);
    }

    @Override
    public String getTableName(final int column) throws SQLException {
        String actualTableName = resultSetMetaData.getTableName(column);
        return actualTableName;
    }

    @Override
    public String getCatalogName(final int column) {
        return DefaultSchema.LOGIC_NAME;
    }

    @Override
    public int getColumnType(final int column) throws SQLException {
        return resultSetMetaData.getColumnType(column);
    }

    @Override
    public String getColumnTypeName(final int column) throws SQLException {
        return resultSetMetaData.getColumnTypeName(column);
    }

    @Override
    public boolean isReadOnly(final int column) throws SQLException {
        return resultSetMetaData.isReadOnly(column);
    }

    @Override
    public boolean isWritable(final int column) throws SQLException {
        return resultSetMetaData.isWritable(column);
    }

    @Override
    public boolean isDefinitelyWritable(final int column) throws SQLException {
        return resultSetMetaData.isDefinitelyWritable(column);
    }

    @Override
    public String getColumnClassName(final int column) throws SQLException {
        return resultSetMetaData.getColumnClassName(column);
    }
}
