package ru.yandex.webmaster3.storage.util.ydb.querybuilder;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.yandex.ydb.table.values.Value;

import ru.yandex.webmaster3.storage.util.ydb.ExecuteQuery;
import ru.yandex.webmaster3.storage.util.ydb.query.Clause;
import ru.yandex.webmaster3.storage.util.ydb.query.Statement;

/**
 * Created by Oleg Bazdyrev on 20/07/2020.
 */
public class Delete extends Statement {
    private Where where = new Where(this);
    private Select<?> onSelect = null;
    private ExecuteQuery executeQuery;

    public Delete(String tablePrefix, String table, ExecuteQuery executeQuery) {
        super(tablePrefix, table, OperationType.DELETE);
        this.executeQuery = executeQuery;
    }

    public Where where(Clause condition) {
        return where.and(condition);
    }

    public Delete on(Select<?> sel) {
        useFirstVersion = true;
        onSelect = sel;
        return this;
    }

    @Override
    public String toQueryString() {
        StringBuilder sb = new StringBuilder();
        sb.append(SYNTAX_FIRST_VERSION).append('\n');
        sb.append(TABLE_PREFIX).append("'").append(tablePrefix).append("';\n");
        
        if (onSelect != null) {
            onSelect.appendDeclarations(sb);
        } else {
            if (!where.list.isEmpty()) {
                where.appendDeclaration(sb);
            }
        }

        sb.append("DELETE FROM ").append(table).append("\n");
        if (onSelect != null) {
            sb.append("ON \n");
            sb.append(onSelect.getQueryBody());
            sb.append("\n");
        } else {
            if (!where.list.isEmpty()) {
                sb.append("WHERE \n");
                where.append(sb);
                sb.append("\n");
            }
        }

        sb.append(";");
        return sb.toString();
    }

    @Override
    public Map<String, Value> getParameters() {
        Map<String, Value> result = new HashMap<>();
        if (onSelect != null) {
            result.putAll(onSelect.getParameters());
        } else {
            where.list.forEach(e -> e.putParameter(result));
        }

        return result;
    }

    public void execute() {
        this.executeQuery.execute(this);
    }

    public class Where {
        private final Delete parent;
        List<Clause> list;
        Map<String, Integer> parameterInfo;
        private int index;

        public Where(Delete parent) {
            this.parent = parent;
            list = new ArrayList<>(6);
            parameterInfo = new HashMap<>(6);
            index++;
        }

        public Statement getStatement() {
            return parent;
        }

        public Where and(Clause condition) {
            index = condition.initIndex(index);
            list.add(condition);
            return this;
        }

        public StringBuilder appendDeclaration(StringBuilder sb) {
            int index = 0;
            Set<Integer> declaredParameter = new HashSet<>();
            for (Clause clause : list) {
                clause.appendDeclaration(sb, declaredParameter);
            }
            return sb;
        }

        StringBuilder append(StringBuilder sb) {
            for (int i = 0; i < list.size(); i++) {
                Clause clause = list.get(i);
                if (i == list.size() - 1) {
                    clause.appendTo(sb);
                    sb.append("\n");
                } else {
                    clause.appendTo(sb);
                    sb.append(" and \n");
                }
            }
            return sb;
        }

        public void execute() {
            this.parent.execute();
        }
    }
}
