package fr.eni.enchere.dal;

import fr.eni.enchere.bll.UserService;
import fr.eni.enchere.bo.Article;
import fr.eni.enchere.bo.SearchArticleCritere;
import fr.eni.enchere.bo.UserProfil;
import fr.eni.enchere.controllers.AccueilController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Repository
@Primary
public class ArticleRepositoryImpl implements ArticleRepository {

    private static final String UPLOAD_DIR = "src/main/resources/static/images/articles";
    private static final Logger logger = LoggerFactory.getLogger(ArticleRepositoryImpl.class);
    private JdbcTemplate jdbcTemplate;
    private NamedParameterJdbcTemplate namedJdbcTemplate;
    private UserService userService;

    private class ArticleRowMapper implements RowMapper<Article> {
        @Override
        public Article mapRow(ResultSet rs, int rowNum) throws SQLException {
            Article article = new Article();
            article.setId(rs.getInt("a.no_article"));
            article.setNom(rs.getString("a.nom_article"));
            article.setDesc(rs.getString("a.description"));
            article.setDateDebutEnch(rs.getDate("a.date_debut_encheres"));
            article.setDateFinEnch(rs.getDate("a.date_fin_encheres"));
            article.setPrixInitial(rs.getFloat("a.prix_initial"));
            article.setPrixVente(rs.getFloat("a.prix_vente"));
            article.setNoUtilisateur(rs.getInt("a.no_utilisateur"));
            article.setNumCategorie(rs.getInt("a.no_categorie"));
            article.setIsDelete(rs.getBoolean("a.isDelete"));
            return article;
        }
    }

    private class HomeArticleRowMapper implements RowMapper<Article> {
        @Override
        public Article mapRow(ResultSet rs, int rowNum) throws SQLException {
            Article article = new Article();
            article.setId(rs.getInt("a.no_article"));
            article.setNom(rs.getString("a.nom_article"));
            article.setDesc(rs.getString("a.description"));
            article.setDateDebutEnch(rs.getDate("a.date_debut_encheres"));
            article.setDateFinEnch(rs.getDate("a.date_fin_encheres"));
            article.setPrixInitial(rs.getFloat("a.prix_initial"));
            article.setPrixVente(rs.getFloat("a.prix_vente"));
            article.setNoUtilisateur(rs.getInt("u.no_utilisateur"));
            article.setPseudoUtilisateur(rs.getString("u.pseudo"));
            article.setNumCategorie(rs.getInt("a.no_categorie"));
            return article;
        }
    }

    public ArticleRepositoryImpl(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedJdbcTemplate, UserService userService) {
        this.jdbcTemplate = jdbcTemplate;
        this.namedJdbcTemplate = namedJdbcTemplate;
        this.userService = userService;
    }

    @Override
    public List<Article> findAllArticle() {
        String sql = "SELECT * FROM ARTICLES_VENDUS a WHERE a.isDelete = 0";
        List<Article> articles = jdbcTemplate.query(sql, new ArticleRowMapper());
        return articles;
    }

    @Override
    public List<Article> findByUser(int id) {
        String sql = "SELECT * FROM ARTICLES_VENDUS WHERE no_utilisateur = ? AND isDelete = 0";
        List<Article> articles = jdbcTemplate.query(sql, new ArticleRowMapper(), id);
        return articles;
    }

    @Override
    public Page<Article> searchArticlePageable(SearchArticleCritere critere, Pageable pageable) {
        StringBuilder sql = new StringBuilder("SELECT DISTINCT a.*, u.* FROM ARTICLES_VENDUS a ");
        sql.append("JOIN UTILISATEURS u ON a.no_utilisateur = u.no_utilisateur ");
        sql.append("LEFT JOIN ENCHERES e ON a.no_article = e.no_article ");
        sql.append("WHERE 1 = 1 AND a.isDelete = 0");
        List<Object> params = new ArrayList<>();

        if (critere.getNoCategorie() != null) {
            sql.append(" AND a.no_categorie = ?");
            params.add(critere.getNoCategorie());
        }
        if (critere.getTitle() != null && !critere.getTitle().isEmpty()) {
            sql.append(" AND a.nom_article LIKE ?");
            params.add('%' + critere.getTitle() + '%');
        }

        if (critere.getVenteOptions() != null && critere.getVenteOptions().length > 0) {
            sql.append(" AND (");
            boolean isFirstCondition = true;
            for (String option : critere.getVenteOptions()) {
                if (option.equals("venteEnCours")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_debut_encheres <= NOW() AND a.date_fin_encheres >= NOW()) ");
                    isFirstCondition = false;
                }
                if (option.equals("ventesNonDebutees")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_debut_encheres > NOW()) ");
                    isFirstCondition = false;
                }
                if (option.equals("ventesTerminees")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_fin_encheres < NOW()) ");
                    isFirstCondition = false;
                }
            }
            sql.append(") AND a.no_utilisateur = ?");
            params.add(critere.getNoVendeur());
        }

        if (critere.getAchatOptions() != null && critere.getAchatOptions().length > 0) {
            sql.append(" AND (");
            boolean isFirstCondition = true;
            for (String option : critere.getAchatOptions()) {
                if (option.equals("encheresOuvertes")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_debut_encheres <= NOW() AND a.date_fin_encheres >= NOW()) ");
                    isFirstCondition = false;
                }
                if (option.equals("enchereEnCours")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (e.no_utilisateur = ? AND a.date_debut_encheres <= NOW() AND a.date_fin_encheres >= NOW()) ");
                    isFirstCondition = false;
                    params.add(critere.getNoVendeur());
                }
                if (option.equals("enchereRemportees")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (e.no_utilisateur = ? AND e.montant_enchere = (SELECT MAX(montant_enchere) FROM ENCHERES WHERE no_article = a.no_article)) ");
                    isFirstCondition = false;
                    params.add(critere.getNoVendeur());
                }
            }

            sql.append(")");
        }

        // Compte le nombre total d'articles
        int totalCount = countArticlePageable(critere);

        // Ajoute la pagination à la requête SQL
        sql.append(" LIMIT ? OFFSET ?");
        params.add(pageable.getPageSize());
        params.add(pageable.getOffset());

        // Exécute la requête paginée
        List<Article> articles = jdbcTemplate.query(sql.toString(), new HomeArticleRowMapper(), params.toArray());

        // Crée une Page<Article> à partir des résultats et des informations de pagination
        return new PageImpl<>(articles, pageable, totalCount);
    }

    public int countArticlePageable(SearchArticleCritere critere) {
        StringBuilder sql = new StringBuilder("SELECT COUNT(a.no_article) FROM ARTICLES_VENDUS a ");
        sql.append("JOIN UTILISATEURS u ON a.no_utilisateur = u.no_utilisateur ");
        sql.append("LEFT JOIN ENCHERES e ON a.no_article = e.no_article ");
        sql.append("WHERE 1 = 1 AND a.isDelete = 0");
        List<Object> params = new ArrayList<>();

        if (critere.getNoCategorie() != null) {
            sql.append(" AND a.no_categorie = ?");
            params.add(critere.getNoCategorie());
        }
        if (critere.getTitle() != null && !critere.getTitle().isEmpty()) {
            sql.append(" AND a.nom_article LIKE ?");
            params.add('%' + critere.getTitle() + '%');
        }

        if (critere.getVenteOptions() != null && critere.getVenteOptions().length > 0) {
            sql.append(" AND (");
            boolean isFirstCondition = true;
            for (String option : critere.getVenteOptions()) {
                if (option.equals("venteEnCours")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_debut_encheres <= NOW() AND a.date_fin_encheres >= NOW()) ");
                    isFirstCondition = false;
                }
                if (option.equals("ventesNonDebutees")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_debut_encheres > NOW()) ");
                    isFirstCondition = false;
                }
                if (option.equals("ventesTerminees")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_fin_encheres < NOW()) ");
                    isFirstCondition = false;
                }
            }
            sql.append(") AND a.no_utilisateur = ?");
            params.add(critere.getNoVendeur());
        }

        if (critere.getAchatOptions() != null && critere.getAchatOptions().length > 0) {
            sql.append(" AND (");
            boolean isFirstCondition = true;
            for (String option : critere.getAchatOptions()) {
                if (option.equals("encheresOuvertes")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (a.date_debut_encheres <= NOW() AND a.date_fin_encheres >= NOW()) ");
                    isFirstCondition = false;
                }
                if (option.equals("enchereEnCours")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (e.no_utilisateur = ? AND a.date_debut_encheres <= NOW() AND a.date_fin_encheres >= NOW()) ");
                    isFirstCondition = false;
                    params.add(critere.getNoVendeur());
                }
                if (option.equals("enchereRemportees")) {
                    if (!isFirstCondition) {
                        sql.append(" OR ");
                    }
                    sql.append(" (e.no_utilisateur = ? AND e.montant_enchere = (SELECT MAX(montant_enchere) FROM ENCHERES WHERE no_article = a.no_article)) ");
                    isFirstCondition = false;
                    params.add(critere.getNoVendeur());
                }
            }

            sql.append(")");
        }

        // Exécute la requête paginée
        int articles = jdbcTemplate.queryForObject(sql.toString(), Integer.class, params.toArray());

        // Retourne le nombre d'articles
        return articles;
    }

    @Override
    public Article findArticleById(int id) {
        String sql = "SELECT * FROM ARTICLES_VENDUS a WHERE a.no_article = ?";
        Article article = jdbcTemplate.queryForObject(sql, new ArticleRowMapper(), id);
        return article;
    }

    @Override
    public List<Article> findArticleByTitle(String title) {
        String sql = "SELECT * FROM ARTICLES_VENDUS a WHERE nom_article LIKE ? AND a.isDelete = 0";
        List<Article> articles = jdbcTemplate.query(sql, new ArticleRowMapper(), "%" + title + "%");
        return articles;
    }

    @Override
    public int saveArticle(Article article) {
        if (article.getId() == 0) {
            //Nouvel article
            String sql = "INSERT INTO ARTICLES_VENDUS (nom_article, description, date_debut_encheres, date_fin_encheres, prix_initial, prix_vente, no_utilisateur, no_categorie, isDelete) " +
                    "VALUES (:nom_article, :description, :date_debut_encheres, :date_fin_encheres, :prix_initial, null, :no_utilisateur, :no_categorie, 0)";
            MapSqlParameterSource parameters = new MapSqlParameterSource();
            parameters.addValue("nom_article", article.getNom());
            parameters.addValue("description", article.getDesc());
            parameters.addValue("date_debut_encheres", article.getDateDebutEnch());
            parameters.addValue("date_fin_encheres", article.getDateFinEnch());
            parameters.addValue("prix_initial", article.getPrixInitial());
            parameters.addValue("no_utilisateur", article.getUtilisateur());
            parameters.addValue("no_categorie", article.getNumCategorie());
            KeyHolder keyHolder = new GeneratedKeyHolder();
            namedJdbcTemplate.update(sql, parameters, keyHolder, new String[] {"no_article"});
            if (keyHolder.getKey() != null) {
                article.setId(keyHolder.getKey().intValue());
            }
            //Enregistrement du fichier
            MultipartFile file = article.getPhoto();
            if (file != null && !file.isEmpty()) {
                try {
                    // Renommer le fichier avec l'ID de l'article
                    String newFileName = article.getId() + ".jpg";
                    // Chemin du dossier de destination
                    Path uploadPath = Paths.get(UPLOAD_DIR);
                    // Créer le dossier s'il n'existe pas
                    Files.createDirectories(uploadPath);
                    // Chemin complet du fichier de destination
                    Path filePath = uploadPath.resolve(newFileName);
                    // Copier le fichier dans le dossier de destination
                    FileCopyUtils.copy(file.getInputStream(), Files.newOutputStream(filePath));
                } catch (IOException e) {
                    e.printStackTrace();
                    // Gérer l'erreur
                }
            }
        }else {
            //Mettre à jour l'article

        }
        return article.getId();
    }

    @Override
    public void deleteArticle(int id) {
        String sql = "UPDATE ARTICLES_VENDUS " +
                "SET isDelete = 1 " +
                "WHERE no_article = ?";
        jdbcTemplate.update(sql, id);
    }

    @Override
    public int updateArticle(Article article) {
        String sql = "UPDATE ARTICLES_VENDUS " +
                "SET nom_article = ?, " +
                "description = ?," +
                "date_debut_encheres = ?," +
                "date_fin_encheres = ?," +
                "prix_initial = ?," +
                "no_categorie = ? " +
                "WHERE no_article = ?";
        jdbcTemplate.update(sql, article.getNom(), article.getDesc(), article.getDateDebutEnch(), article.getDateFinEnch(), article.getPrixInitial(), article.getNumCategorie(), article.getId());
        return article.getId();
    }

    @Override
    public void setSellPrice(int id, float price) {
        String sql = "UPDATE ARTICLES_VENDUS SET prix_vente = ?, isDelete = 1 WHERE no_article = ?";
        jdbcTemplate.update(sql, price, id);
    }
}
