package com.salpa.subject.domain.provider.jdbc;

import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.data.TableMeta;
import com.salpa.subject.domain.provider.*;
import com.salpa.subject.domain.provider.condition.Condition;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Slf4j
public class JdbcTableMetaProvider implements TableMetaProvider {

    private final ColumnMetaProvider columnMetaProvider;

    private final IndexMetaProvider indexMetaProvider;

    private final TriggerMetaProvider triggerMetaProvider;

    private final ForeignKeyMetaProvider foreignKeyMetaProvider;

    @Override
    public List<TableMeta> selectTables(Connection connection, Condition condition) {
        try {
            return doSelect(connection, condition);
        } catch (SQLException e) {
            throw new IllegalStateException(e);
        }
    }

    private List<TableMeta> doSelect(Connection connection, Condition condition) throws SQLException {
        List<TableMeta> tableMetas = new ArrayList<>();
        String databaseName = condition.getDatabaseName();
        String[] tableTypes = {"TABLE", "VIEW"};
        ResultSet tablesResult = connection.getMetaData()
                .getTables(databaseName, condition.getSchemaName(), null, tableTypes);
        try {
            while (tablesResult.next()) {
                String tableName = tablesResult.getString("TABLE_NAME");
                if (condition.tableIsIgnored(tableName)) {
                    if (log.isWarnEnabled()) {
                        log.warn("ignored table: " + databaseName + "." + tableName);
                    }
                } else {
                    String tableType = tablesResult.getString("TABLE_TYPE");
                    String tableComment = tablesResult.getString("REMARKS");
                    TableCondition tableCondition = TableCondition.of(condition, tableName);
                    List<ColumnMeta> columns = columnMetaProvider.selectColumns(connection, tableCondition);
                    if (columns.isEmpty()) {
                        if (log.isWarnEnabled()) {
                            log.warn("ignored table: " + databaseName + "." + tableName
                                    + ", caused by get empty columns");
                        }
                        continue;
                    }
                    TableMeta tableMeta = TableMeta.builder()
                            .name(tableName)
                            .type(tableType)
                            .comment(tableComment)
                            .columns(columns)
                            .foreignKeys(foreignKeyMetaProvider.selectForeignKeys(connection, tableCondition))
                            .indexes(indexMetaProvider.selectIndexes(connection, tableCondition))
                            .triggers(triggerMetaProvider.selectTriggers(connection, tableCondition))
                            .build();
                    tableMetas.add(tableMeta);
                }
            }
        } finally {
            tablesResult.close();
        }
        return tableMetas;
    }

}
