package f1.db;

import f1.entity.Driver;
import f1.entity.DriverHistoryItem;
import f1.entity.DriverStatistic;
import f1.entity.SeasonDriver;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DriverDao extends BaseDao {

    public DriverDao(Database db) {
        super(db);
    }

    public ArrayList<Driver> getAllDrivers() {
        ArrayList<Driver> drivers = new ArrayList<>();
        try (var con = getConnection();
                var stmt = con.createStatement();
                var rs = stmt.executeQuery("SELECT * FROM driver")) {
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String country = rs.getString("country");
                java.sql.Date birthday = rs.getDate("birthday");
                drivers.add(new Driver(id, name, country, birthday));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return drivers;
    }

    public ArrayList<SeasonDriver> getDriversBySeason(int season) {
        ArrayList<SeasonDriver> seasonDrivers = new ArrayList<>();
        try (var con = getConnection();
                var stmt = con
                        .prepareStatement("SELECT * FROM season_driver WHERE season = ? ORDER BY team, car_num")) {
            stmt.setInt(1, season);
            var rs = stmt.executeQuery();
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String team = rs.getString("team");
                String country = rs.getString("country");
                java.sql.Date birthday = rs.getDate("birthday");
                int carNum = rs.getInt("car_num");
                int seasonVal = rs.getInt("season");
                seasonDrivers.add(new SeasonDriver(id, name, team, country, birthday, carNum, seasonVal));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return seasonDrivers;
    }

    public List<Map<String, Object>> getSeasonTop5DriverStandings(int season) {
        List<Map<String, Object>> result = new ArrayList<>();
        String sql = "SELECT sd.name, sd.team, standings.total_score, standings.ranking " +
                     "FROM season_driver sd " +
                     "JOIN ( " +
                     "    SELECT rr.driver_id, SUM(rr.score) AS total_score, " +
                     "           RANK() OVER (ORDER BY SUM(rr.score) DESC) AS ranking " +
                     "    FROM race_result rr " +
                     "    JOIN prix p ON rr.prix_id = p.id " +
                     "    WHERE p.season = ? " +
                     "    GROUP BY rr.driver_id " +
                     ") standings ON sd.id = standings.driver_id " +
                     "WHERE sd.season = ? " +
                     "ORDER BY standings.ranking ASC " +
                     "LIMIT 5";
        
        try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
            stmt.setInt(1, season);
            stmt.setInt(2, season);
            var rs = stmt.executeQuery();
            while (rs.next()) {
                Map<String, Object> item = new HashMap<>();
                item.put("driver_name", rs.getString("name"));
                item.put("team_name", rs.getString("team"));
                item.put("total_score", rs.getInt("total_score"));
                item.put("ranking", rs.getInt("ranking"));
                result.add(item);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }

    public ArrayList<String> getDriverNamesByTeamAndSeason(int teamId, int season) {
        ArrayList<String> names = new ArrayList<>();
        String sql = "SELECT d.name FROM driver d JOIN contract c ON d.id = c.driver_id WHERE c.season = ? AND c.team_id = ?";
        try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
            stmt.setInt(1, season);
            stmt.setInt(2, teamId);
            var rs = stmt.executeQuery();
            while (rs.next()) {
                names.add(rs.getString("name"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return names;
    }

    public DriverStatistic getDriverStatistic(int driverId, int season, boolean isSprint) {
        return getStatistic("rr.driver_id = ?", driverId, season, isSprint);
    }

    private DriverStatistic getStatistic(String whereClause, int id, int season, boolean isSprint) {
        DriverStatistic statistic = null;
        String sql = """
                SELECT
                    COUNT(DISTINCT prix_id) AS total_cnt,
                    SUM(score) AS score_sum,
                    SUM(end_position <= 3) AS medal,
                    SUM(end_position = 1) AS gold,
                    SUM(start_position = 1) AS pole,
                    SUM(end_position <= 10) AS top_ten,
                    SUM(finish_time IN ('DNF', 'DNS', 'DSQ')) AS unfinished,
                    SUM(fastest_time = fastest.fastest) AS fastest_lap
                FROM
                    race_result rr
                NATURAL JOIN (
                    SELECT
                        rr.prix_id ,
                        MIN(rr.fastest_time) AS fastest
                    FROM
                        race_result rr
                    WHERE
                        rr.fastest_time NOT IN ('DNF', 'DNS', 'DSQ')
                        AND rr.is_sprint = ?
                        AND rr.prix_id IN (SELECT id FROM prix WHERE season = ?)
                    GROUP BY
                        rr.prix_id) fastest
                WHERE
                    %s
                    AND rr.is_sprint = ?
                    AND rr.prix_id IN (SELECT id FROM prix WHERE season = ?)
                """.formatted(whereClause);
        try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
            stmt.setBoolean(1, isSprint);
            stmt.setInt(2, season);
            stmt.setInt(3, id);
            stmt.setBoolean(4, isSprint);
            stmt.setInt(5, season);
            var rs = stmt.executeQuery();
            if (rs.next()) {
                int totalCnt = rs.getInt("total_cnt");
                int scoreSum = rs.getInt("score_sum");
                int medal = rs.getInt("medal");
                int gold = rs.getInt("gold");
                int pole = rs.getInt("pole");
                int topTen = rs.getInt("top_ten");
                int unfinished = rs.getInt("unfinished");
                int fastestLap = rs.getInt("fastest_lap");
                statistic = new DriverStatistic(totalCnt, scoreSum, medal, gold, pole, topTen, unfinished,
                        fastestLap);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return statistic;
    }

    public ArrayList<DriverHistoryItem> getDriverHistory(int driverId, int season) {
        ArrayList<DriverHistoryItem> history = new ArrayList<>();
        String sql = "SELECT prix_name, team_name, pos, score, is_sprint FROM prix_result WHERE driver_id = ? AND season = ? ORDER BY round, is_sprint";
        try (var con = getConnection(); var stmt = con.prepareStatement(sql)) {
            stmt.setInt(1, driverId);
            stmt.setInt(2, season);
            var rs = stmt.executeQuery();
            while (rs.next()) {
                history.add(new DriverHistoryItem(
                        rs.getString("prix_name"),
                        rs.getString("team_name"),
                        rs.getInt("pos"),
                        rs.getInt("score"),
                        rs.getBoolean("is_sprint")));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return history;
    }
}
