package eu.dnetlib.openaire.user.dao;

import eu.dnetlib.openaire.user.Role;
import eu.dnetlib.openaire.user.store.DataSourceConnector;
import eu.dnetlib.openaire.user.store.Statement;
import org.springframework.beans.factory.annotation.Autowired;

import javax.sql.DataSource;

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

import static eu.dnetlib.openaire.user.queries.RoleQueries.*;

/**
 * Created by sofia on 8/11/2016.
 */
public class RoleDAO {

    private final DataSource ds;

    @Autowired
    private DataSourceConnector dataSourceConnector;

    public RoleDAO() {
        this.ds = dataSourceConnector.getDatasource();
    }

    // FETCH

    public List<Role> fetchAll()
            throws SQLException
    {
        return (List<Role>) executeQuery(FETCH_ALL);
    }

    public Role fetchById(final int id)
            throws SQLException
    {
        List<Role> roles = executeQuery(FETCH_BY_ID, new Statement.Initializer() {
            @Override
            public void prepare(PreparedStatement stmt) throws SQLException {
                stmt.setInt(1, id);
            }
        });

        if (roles.isEmpty())
            return null;

        return roles.get(0);
    }

    public Role fetchByRole(final String role)
            throws SQLException
    {
        List<Role> roles = executeQuery(FETCH_BY_ROLE, new Statement.Initializer() {
            @Override
            public void prepare(PreparedStatement stmt) throws SQLException {
                stmt.setString(1, role);
            }
        });

        if (roles.isEmpty())
            return null;

        return roles.get(0);
    }

    public long countAll() throws SQLException
    {
        return executeCount(COUNT_ALL);
    }


    public boolean insert(final Role role)
            throws SQLException
    {
        return executeUpdate(INSERT, new Statement.Initializer() {
            @Override
            public void prepare(PreparedStatement stmt)
                    throws SQLException {
                stmt.setString(1, role.getRole());
            }
        }) > 0;

    }

    public boolean delete(final String role)
            throws SQLException
    {
        return executeUpdate(DELETE, new Statement.Initializer() {
            @Override
            public void prepare(PreparedStatement stmt)
                    throws SQLException {
                stmt.setString(1, role);
            }
        }) > 0;
    }

    public boolean update(final Role role)
            throws SQLException
    {
        return executeUpdate(UPDATE, new Statement.Initializer() {
            @Override
            public void prepare(PreparedStatement stmt)
                    throws SQLException {
                stmt.setString(1, role.getRole());
            }
        }) > 0;
    }

    protected Role fromResultSet(ResultSet set)
            throws SQLException
    {
        Role role = new Role(set.getString("role"));

        role.setId(set.getInt("id"));

        return role;
    }

    protected int executeUpdate(String sql, Statement.Initializer init)
            throws SQLException
    {
        Connection connection = ds.getConnection();

        try {
            PreparedStatement stmt = connection.prepareStatement(sql);
            init.prepare(stmt);
            return stmt.executeUpdate();
        } finally {
            connection.close();
        }
    }

    protected List<Role> executeQuery(String sql, Statement.Initializer init)
            throws SQLException
    {
        Connection connection = ds.getConnection();

        try {
            PreparedStatement stmt = connection.prepareStatement(sql);
            init.prepare(stmt);

            ResultSet set = stmt.executeQuery();

            try {
                List<Role> results = new ArrayList<>();

                while (set.next())
                    results.add(fromResultSet(set));

                return results;
            } finally {
                set.close();
            }
        } finally {
            connection.close();
        }
    }

    protected int executeUpdate(String sql)
            throws SQLException
    {
        return executeUpdate(sql, Statement.emptyInitializer());
    }

    protected List<Role> executeQuery(String sql)
            throws SQLException
    {
        return executeQuery(sql, Statement.emptyInitializer());
    }

    public long executeCount(String sql)
            throws SQLException
    {
        return executeCount(sql, Statement.emptyInitializer());
    }

    public long executeCount(String sql, Statement.Initializer init)
            throws SQLException
    {
        Connection connection = ds.getConnection();

        try {
            PreparedStatement stmt = connection.prepareStatement(sql);
            init.prepare(stmt);

            ResultSet set = stmt.executeQuery();

            try {
                if (set.next())
                    return set.getLong(1);
                throw new IllegalArgumentException(stmt.toString());
            } finally {
                set.close();
            }
        } finally {
            connection.close();
        }
    }
}
