package com.conveyal.gtfs.graphql.fetchers;

import com.conveyal.gtfs.graphql.GTFSGraphQL;
import com.conveyal.gtfs.loader.JDBCTableReader;
import graphql.Scalars;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLFieldDefinition;
import org.apache.commons.dbutils.DbUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;

import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;

/**
 * Get quantity of rows in the given table.
 */
public class RowCountFetcher implements DataFetcher {

    public static final Logger LOG = LoggerFactory.getLogger(RowCountFetcher.class);

    private final String tableName;

    public RowCountFetcher(String tableName) {
        this.tableName = tableName;
    }

    @Override
    public Integer get(DataFetchingEnvironment environment) {
        Map<String, Object> parentFeedMap = environment.getSource();
        String namespace = (String) parentFeedMap.get("namespace");
        Connection connection = null;
        try {
            connection = GTFSGraphQL.getConnection();
            Statement statement = connection.createStatement();
            String sql = String.format("select count(*) from %s.%s", namespace, tableName);
            if (statement.execute(sql)) {
                ResultSet resultSet = statement.getResultSet();
                resultSet.next();
                return resultSet.getInt(1);
            }
        } catch (SQLException e) {
            // In case the table doesn't exist in this feed, just return zero and don't print noise to the log.
            // Unfortunately JDBC doesn't seem to define reliable error codes.
            if (! JDBCTableReader.SQL_STATE_UNDEFINED_TABLE.equals(e.getSQLState())) {
                e.printStackTrace();
            }
        } finally {
            DbUtils.closeQuietly(connection);
        }
        return 0;
    }

    /**
     * Convenience method to create a field in a GraphQL schema that fetches the number of rows in a table.
     * Must be on a type that has a "namespace" field for context.
     */
    public static GraphQLFieldDefinition field (String fieldName, String tableName) {
        return newFieldDefinition()
                .name(fieldName)
                .type(Scalars.GraphQLInt)
                .dataFetcher(new RowCountFetcher(tableName))
                .build();
    }

    /**
     * For cases where the GraphQL field name is the same as the table name itself.
     */
    public static GraphQLFieldDefinition field (String tableName) {
        return field(tableName, tableName);
    }

}
@@ -822,16 +822,54 @@
 bleName;
+%0A    private final String filterField;
 %0A%0A    pu
@@ -925,30 +925,175 @@
 this
-.tableName = tableName
+(tableName, null);%0A    %7D%0A%0A    public RowCountFetcher(String tableName, String filterField) %7B%0A        this.tableName = tableName;%0A        this.filterField = filterField
 ;%0A  
@@ -1568,24 +1568,392 @@
 tableName);%0A
+            if (filterField != null) %7B%0A                // FIXME Does this handle null cases?%0A                // Add where clause to filter out non-matching results.%0A                String filterValue = (String) parentFeedMap.get(filterField);%0A                sql += String.format(%22 where %25s = '%25s'%22, filterField, filterValue);%0A            %7D%0A            LOG.info(sql);%0A
             
@@ -3077,32 +3077,824 @@
 build();%0A    %7D%0A%0A
+    /**%0A     * Convenience method to create a field in a GraphQL schema that fetches the number of rows in a table with a filter%0A     * or where clause. If a filter field is provided, count only the rows that match the parent entity's value for the%0A     * given field. For example, adding a trip_count field to routes (filter field route_id) would add a trip count to%0A     * each route entity with the number of trips that operate under each route's route_id.%0A     */%0A    public static GraphQLFieldDefinition field (String fieldName, String tableName, String filterField) %7B%0A        return newFieldDefinition()%0A                .name(fieldName)%0A                .type(Scalars.GraphQLInt)%0A                .dataFetcher(new RowCountFetcher(tableName, filterField))%0A                .build();%0A    %7D%0A%0A
     /**%0A     * F

add filtered row counts