package com.sifang.modules.sys.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * build sql util.
 */
public class SqlBuilder {

    private final StringBuilder sb = new StringBuilder();
    private final List<Object> params = new ArrayList<>();

    private static final Pattern IN_PATT = Pattern.compile("(.*?\\s+)?in\\s*$", Pattern.CASE_INSENSITIVE);

    public SqlBuilder line(String line, Object... params) {
        sb.append(" ").append(line).append(" ");
        if (params != null && params.length > 0) {
            this.params.addAll(Arrays.asList(params));
        }

        return this;
    }

    /**
     * @param line make sure line is something like: xx like ?
     */
    public SqlBuilder like(String line, String param) {
        if (param == null || "".equals(param)) {
            throw new RuntimeException("param required for LIKE statement");
        }
        line(line, "%" + param + "%");
        return this;
    }

    /**
     * in line should end with "in".
     */
    public SqlBuilder inst(String line, String... params) {
        if (params == null || params.length < 1) {
            throw new IllegalArgumentException("at least one param is required for IN statement");
        }

        inst(line, Arrays.asList(params));
        return this;
    }

    /**
     * in line should end with "in".
     */
    public SqlBuilder inst(String line, List<String> params) {
        if (params == null || params.size() < 1) {
            throw new IllegalArgumentException("at least one param is required for IN statement");
        }
        if (!isEndWithIn(line)) {
            throw new IllegalArgumentException("statement should ends with in.");
        }

        line(line + "(" + SqlUtils.inParam(params) + ")");
        return this;
    }

    public SqlBuilder inst(String line, Set<String> params) {
        return inst(line, new ArrayList<>(params));
    }

    public SqlBuilder limit(int pageNo, int pageSize) {
        line(String.format("limit %s offset %s", pageSize, (pageNo - 1) * pageSize));
        return this;
    }

    public String build() {
        return sb.toString();
    }

    public String toString() {
        return build();
    }

    public Object[] params() {
        return this.params.toArray();
    }

    static boolean isEndWithIn(String statement) {
        return IN_PATT.matcher(statement).matches();
    }

}
