package mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.jetty.util.log.Log;

import com.google.common.base.Strings;

import object.CurrentParsing;
import object.ResultDTO;

public class Mysql {

    private static final Mysql instance = new Mysql();

    private static final String USERNAME = "OddsNy";
    private static final String PASSWORD = "Odds1_Ny_Password";
    private static final String DATABASE = "new_odds";
    private static final String URL = "jdbc:mysql://nordh.xyz:3306/";
    private static final String TIMEZONE_FIX = "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";

    private Connection conn;

    protected Mysql() {
        getConnection();
    }

    public static Mysql getInstance() {
        return instance;
    }

    public int addCountry(String name) throws SQLException {
        name = name.replace(" ", "-");
        name = name.replace("\\.", "");
        final String sql = "INSERT INTO Country (name) VALUES (?) ON DUPLICATE KEY UPDATE name = ?";
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, name);
            stat.setString(2, name);
            stat.executeUpdate();
        }

        return getId("Country", name, -1, -1);
    }

    public int addLeague(String leagueName, String country, String sport) {
        int countryId = -1;
        try {
            leagueName = leagueName.trim();
            leagueName = leagueName.replace(" ", "-");
            leagueName = leagueName.replace("\\.", "");
            final int sportId = addSport(sport);
            countryId = addCountry(country);
            final String sql = "INSERT INTO League (name, sportId, countryId) VALUES (?, ?, ?) "
                    + "ON DUPLICATE KEY UPDATE name = ?";
            try (PreparedStatement stat = conn.prepareStatement(sql)) {
                stat.setString(1, leagueName);
                stat.setInt(2, sportId);
                stat.setInt(3, countryId);
                stat.setString(4, leagueName);

                stat.executeUpdate();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return getId("League", leagueName, countryId, -1);
    }

    public void addResult(ResultDTO result) {
        if (result.getCountryId() < 0 || result.getLeagueId() < 0) {
            System.out.println(String.format("Must supply countryId %s and leagueId %s", result.getCountryId(),
                    result.getLeagueId()));
            return;
        }
        try {
            int homeTeamId = getOrInsertTeam(result.getHomeTeam(), result.getCountryId(), result.getLeagueId(),
                    result.getSportId());

            final int awayTeamId = getOrInsertTeam(result.getAwayTeam(), result.getCountryId(), result.getLeagueId(),
                    result.getSportId());

            final String selectSql = "SELECT id FROM SoccerResults WHERE homeTeamId = ? AND awayTeamId = ? AND DATE(gameDate) = DATE(?)";
            final ResultSet rs;
            int gameId = -1;
            final String date = result.getGameDate().format(DateTimeFormatter.ISO_DATE);
            try (PreparedStatement stat = conn.prepareStatement(selectSql)) {
                stat.setInt(1, homeTeamId);
                stat.setInt(2, awayTeamId);
                stat.setString(3, date);

                rs = stat.executeQuery();
                while (rs.next()) {
                    gameId = rs.getInt("id");
                }
            }

            if (gameId != -1) {
                final String sql = "UPDATE " + result.getTableName()
                        + " SET homeScore = ?, awayScore = ?, overtime = ?, odds1 = ?, oddsX = ?, odds2 = ? "
                        + "WHERE homeTeamId = ? AND awayTeamId = ? AND DATE(gameDate) = ?";

                try (PreparedStatement stat = conn.prepareStatement(sql)) {
                    stat.setInt(1, result.getHomeScore());
                    stat.setInt(2, result.getAwayScore());
                    stat.setBoolean(3, result.isOvertime());
                    stat.setFloat(4, result.getOdds1());
                    stat.setFloat(5, result.getOddsX());
                    stat.setFloat(6, result.getOdds2());
                    stat.setInt(7, homeTeamId);
                    stat.setInt(8, awayTeamId);
                    stat.setString(9, date);

                    stat.executeUpdate();
                }

            } else {
                final String sql = "INSERT INTO " + result.getTableName()
                        + " (homeTeamId, awayTeamId, homeScore, awayScore, overtime, odds1, oddsX, odds2, countryId, gameDate, season, leagueId) "
                        + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE homeScore = ?, awayScore = ?, odds1 = ?, oddsX = ?, odds2 = ?";
                try (PreparedStatement stat = conn.prepareStatement(sql)) {
                    stat.setInt(1, homeTeamId);
                    stat.setInt(2, awayTeamId);
                    stat.setInt(3, result.getHomeScore());
                    stat.setInt(4, result.getAwayScore());
                    stat.setBoolean(5, result.isOvertime());
                    stat.setFloat(6, result.getOdds1());
                    stat.setFloat(7, result.getOddsX());
                    stat.setFloat(8, result.getOdds2());
                    stat.setInt(9, result.getCountryId());
                    stat.setString(10, result.getGameDate().toString());
                    stat.setString(11, result.getSeason());
                    stat.setInt(12, result.getLeagueId());
                    stat.setInt(13, result.getHomeScore());
                    stat.setInt(14, result.getAwayScore());
                    stat.setFloat(15, result.getOdds1());
                    stat.setFloat(16, result.getOddsX());
                    stat.setFloat(17, result.getOdds2());

                    stat.execute();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void addResults(List<ResultDTO> resultsToInsert) {

        final String sql = "INSERT INTO SoccerResults (homeTeamId, awayTeamId, homeScore, awayScore, overtime, odds1, oddsX, odds2, countryId, gameDate, season, leagueId) "
                + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE homeScore = ?, awayScore = ?, odds1 = ?, oddsX = ?, odds2 = ?";
        try (PreparedStatement stat = conn.prepareStatement(sql)) {

            for (ResultDTO resultDTO : resultsToInsert) {

                if (resultDTO.getHomeTeamId() < 0 || resultDTO.getAwayTeamId() < 0 || resultDTO.getLeagueId() < 0
                        || resultDTO.getCountryId() < 0) {

                    Logger.getGlobal().log(Level.WARNING, resultDTO.toString());
                } else {
                    stat.setInt(1, resultDTO.getHomeTeamId());
                    stat.setInt(2, resultDTO.getAwayTeamId());
                    stat.setInt(3, resultDTO.getHomeScore());
                    stat.setInt(4, resultDTO.getAwayScore());
                    stat.setBoolean(5, resultDTO.isOvertime());
                    stat.setFloat(6, resultDTO.getOdds1());
                    stat.setFloat(7, resultDTO.getOddsX());
                    stat.setFloat(8, resultDTO.getOdds2());
                    stat.setInt(9, resultDTO.getCountryId());
                    stat.setString(10, resultDTO.getGameDate().toString());
                    stat.setString(11, resultDTO.getSeason());
                    stat.setInt(12, resultDTO.getLeagueId());
                    stat.setInt(13, resultDTO.getHomeScore());
                    stat.setInt(14, resultDTO.getAwayScore());
                    stat.setFloat(15, resultDTO.getOdds1());
                    stat.setFloat(16, resultDTO.getOddsX());
                    stat.setFloat(17, resultDTO.getOdds2());

                    stat.addBatch();
                }
            }

            stat.executeBatch();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public int addSport(String sport) throws SQLException {
        sport = sport.replace(" ", "-");
        sport = sport.replace("\\.", "");
        final String sql = "INSERT INTO Sport (name) VALUES (?) ON DUPLICATE KEY UPDATE name = ?";
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, sport);
            stat.setString(2, sport);
            stat.executeUpdate();
        }
        return getId("Sport", sport, -1, -1);
    }

    public int getCountryId(String country) {
        final String sql = "SELECT id from Country WHERE name = ?";
        int id = -1;
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, country.trim());

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                id = rs.getInt("id");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return id;
    }

    public CurrentParsing getCurrentParsing() {
        final CurrentParsing returnValue = new CurrentParsing();
        final String sql = "SELECT * FROM parsing";
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                returnValue.setDone(rs.getBoolean("done"));
                returnValue.setCurrentYear(rs.getInt("year"));
                returnValue.setCurrentDate(rs.getDate("gameDate"));
                returnValue.setLeague(rs.getString("league"));
                returnValue.setPage(rs.getInt("page"));
            }
        } catch (final SQLException e) {
            e.printStackTrace();
        }
        return returnValue;
    }

    public Connection getDbConnection() {
        if (conn == null) {
            conn = getConnection();
        }
        return conn;
    }

    public String getLastParsedYear(String leagueName, int countryId) {
        String returnValue = "";

        final String sql = "SELECT parsedYear FROM League WHERE name = ? AND countryId = ?";
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, leagueName);
            stat.setInt(2, countryId);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue = rs.getString("parsedYear");
            }
        } catch (final SQLException e) {
            e.printStackTrace();
        }

        return returnValue;
    }

    public String getLastSeason(int leagueId, int countryId) {
        String result = "";
        String sql = "SELECT season FROM SoccerResults WHERE leagueId = ? AND countryId = ? ORDER BY gameDate DESC limit 1";

        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
            stat.setInt(1, leagueId);
            stat.setInt(2, countryId);

            ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                result = rs.getString("season");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return result;
    }

    public String getLastSeason(String leagueName, int countryId) {
        String sql = "SELECT season FROM SoccerResults WHERE leagueId = (SELECT id FROM League WHERE name = ? AND countryId = ?) ORDER BY gameDate DESC limit 1";
        String result = null;
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, leagueName);
            stat.setInt(2, countryId);

            ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                result = rs.getString("season");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        if (Strings.isNullOrEmpty(result)) {
            result = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy"));
        }
        return result;
    }

    public int getLeagueId(int sportId, int countryId, String leagueName) {
        final String sql = "SELECT id FROM League WHERE name = ? AND countryId = ? AND sportId = ?";
        int id = -1;
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, leagueName.trim());
            stat.setInt(2, countryId);
            stat.setInt(3, sportId);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                id = rs.getInt("id");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        if (id == -1) {
            String message = "Cound not find league " + leagueName + " countryId " + countryId + " sportId " + sportId;
            Logger.getGlobal().log(Level.WARNING, message);
        }
        return id;
    }

    public int getOrInsertTeam(String teamName, int countryId, int leagueId, int sportId) {
        teamName = teamName.replace('\u00A0', ' ').trim();
        int teamId;
        if (leagueId > 0) {

            teamId = getId("Team", teamName, countryId, leagueId);
            if (teamId <= 0) {
                final String insertSql = "INSERT INTO Team (name, sportId, countryId, leagueId) VALUES (? ,? ,? ,?)";
                try (PreparedStatement stat = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
                    stat.setString(1, teamName.trim());
                    stat.setInt(2, sportId);
                    stat.setInt(3, countryId);
                    stat.setInt(4, leagueId);

                    stat.executeUpdate();
                    final ResultSet generatedKeys = stat.getGeneratedKeys();
                    generatedKeys.next();
                    teamId = generatedKeys.getInt(1);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        } else {
            Log.getLog().info("No league Specified for " + teamName + " with countryId " + countryId);
            teamId = -1;
        }

        return teamId;
    }

    public int getSportId(String sportName) {
        final String sql = "SELECT id from Sport WHERE name = ?";
        int id = 0;
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, sportName.trim());

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                id = rs.getInt("id");
            }
        } catch (final SQLException e) {
            e.printStackTrace();
        }
        return id;
    }

    public void setParsingForLeague(int leagueId, int sportId, int countryId, LocalDateTime gameDate,
            int currentParsePage, String parsedYear) {
        final String sql = "UPDATE League SET parsedYear = ?, parsedPage = ?, lastParsedGameDate = ? WHERE sportId = ? AND countryId = ? AND id = ?";
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, parsedYear);
            stat.setInt(2, currentParsePage);
            stat.setString(3, gameDate.toString());
            stat.setInt(4, sportId);
            stat.setInt(5, countryId);
            stat.setInt(6, leagueId);

            stat.executeUpdate();
        } catch (final SQLException e) {
            Log.getLog().info("Failing sql: %s, %s, %s, %s, %s, %s, %s", sql, parsedYear, currentParsePage,
                    gameDate.toString(), sportId, countryId,
                    leagueId);
            e.printStackTrace();
        }
    }

    protected Connection getConnection() {
        if (conn == null) {
            try {
                conn = DriverManager.getConnection(URL + DATABASE + TIMEZONE_FIX, USERNAME, PASSWORD);
            } catch (final SQLException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return conn;
    }

    private int getId(String table, String name, int countryId, int leagueId) {
        String sql = "SELECT id FROM " + table + " WHERE name = ?";
        int id = -1;
        if (countryId > -1) {
            sql += " AND countryId = ?";
        }
        if (leagueId > -1) {
            sql += " AND leagueId = ?";
        }
        try (PreparedStatement stat = conn.prepareStatement(sql)) {
            stat.setString(1, name.trim());
            if (countryId > -1 && leagueId > -1) {
                stat.setInt(2, countryId);
                stat.setInt(3, leagueId);
            } else if (countryId > -1) {
                stat.setInt(2, countryId);
            } else if (leagueId > -1) {
                stat.setInt(2, leagueId);
            }
            final ResultSet insertRs = stat.executeQuery();
            if (insertRs.next()) {
                id = insertRs.getInt("id");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return id;
    }
}
