package de.avatic.lcc.repositories;

import de.avatic.lcc.model.db.materials.Material;
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

@Repository
public class MaterialRepository {

    JdbcTemplate jdbcTemplate;

    @Autowired
    public MaterialRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    private static String buildCountQuery(String filter, boolean excludeDeprecated) {
        StringBuilder queryBuilder = new StringBuilder("""
                SELECT count(*)
                        FROM material WHERE 1=1""");

        if (excludeDeprecated) {
            queryBuilder.append(" AND is_deprecated = FALSE");
        }
        if (filter != null) {
            queryBuilder.append(" AND (name LIKE ? OR part_number LIKE ?) ");
        }

        return queryBuilder.toString();
    }

    private static String buildQuery(String filter, boolean excludeDeprecated) {
        StringBuilder queryBuilder = new StringBuilder("""
                SELECT id, name, part_number, normalized_part_number, hs_code, is_deprecated
                        FROM material WHERE 1=1""");

        if (excludeDeprecated) {
            queryBuilder.append(" AND is_deprecated = FALSE");
        }
        if (filter != null) {
            queryBuilder.append(" AND (name LIKE ? OR part_number LIKE ? ) ");
        }
        queryBuilder.append(" ORDER BY normalized_part_number LIMIT ? OFFSET ?");
        return queryBuilder.toString();
    }

    public List<Material> listAllMaterials() {
        String query = "SELECT * FROM material ORDER BY normalized_part_number";
        return jdbcTemplate.query(query, new MaterialMapper());
    }

    public List<Material> getByPartNumbers(Collection<String> partNumbers) {
        // Handle null or empty case
        if (partNumbers == null || partNumbers.isEmpty()) {
            return Collections.emptyList();
        }

        List<Object> params = new ArrayList<>();
        params.addAll(partNumbers);
        params.addAll(partNumbers);

        String placeholders = String.join(",", Collections.nCopies(partNumbers.size(), "?"));
        String query = "SELECT * FROM material WHERE part_number IN (" + placeholders + ") OR normalized_part_number IN (" + placeholders + ")";

        return jdbcTemplate.query(query, new MaterialMapper(), params.toArray());
    }

    public Optional<Material> getByPartNumber(String partNumber) {
        if (partNumber == null) {
            return Optional.empty();
        }

        String query = "SELECT * FROM material WHERE part_number = ? OR normalized_part_number = ?";

        var material = jdbcTemplate.query(query, new MaterialMapper(), partNumber, partNumber);

        if(material.isEmpty())
            return Optional.empty();

        return Optional.ofNullable(material.getFirst());
    }



    @Transactional
    public Optional<Integer> setDeprecatedById(Integer id) {
        String query = "UPDATE material SET is_deprecated = TRUE WHERE id = ?";
        return Optional.ofNullable(jdbcTemplate.update(query, id) == 0 ? null : id);
    }

    @Transactional
    public SearchQueryResult<Material> listMaterials(Optional<String> filter, boolean excludeDeprecated, SearchQueryPagination pagination) {

        String query = buildQuery(filter.orElse(null), excludeDeprecated);

        var materials = filter.isPresent() ?
                jdbcTemplate.query(query, new MaterialMapper(),
                 filter.get() + "%", filter.get() + "%", pagination.getLimit(), pagination.getOffset()) :
                jdbcTemplate.query(query, new MaterialMapper(),
                        pagination.getLimit(), pagination.getOffset());

        String countQuery = buildCountQuery(filter.orElse(null), excludeDeprecated);

        Integer totalCount = filter.isPresent() ?
                jdbcTemplate.queryForObject(countQuery, Integer.class,
                         filter.get() + "%",  filter.get() + "%") :
                jdbcTemplate.queryForObject(countQuery, Integer.class);

        return new SearchQueryResult<>(materials, pagination.getPage(), totalCount, pagination.getLimit());
    }

    @Transactional
    public Optional<Material> getByIdIncludeDeprecated(Integer id) {
        String query = "SELECT * FROM material WHERE id = ?";

        var material = jdbcTemplate.query(query, new MaterialMapper(), id);

        if(material.isEmpty())
            return Optional.empty();

        return Optional.ofNullable(material.getFirst());
    }

    @Transactional
    public Optional<Material> getById(Integer id) {
        String query = "SELECT * FROM material WHERE id = ? AND is_deprecated = FALSE";

        var material = jdbcTemplate.query(query, new MaterialMapper(), id);

        if(material.isEmpty())
            return Optional.empty();

        return Optional.ofNullable(material.getFirst());
    }

    @Transactional
    public void deleteById(Integer id) {
        String deleteQuery = "UPDATE material SET is_deprecated = TRUE WHERE id = ?";
        jdbcTemplate.update(deleteQuery, id);
    }

    @Transactional
    public Optional<Integer> update(Material material) {
        String updateQuery = "UPDATE material SET name = ?, part_number = ?, normalized_part_number = ?, hs_code = ? WHERE id = ?";
        return Optional.ofNullable(jdbcTemplate.update(updateQuery, material.getName(), material.getPartNumber(), material.getNormalizedPartNumber(), material.getHsCode(), material.getId()) == 0 ? null : material.getId());
    }

    @Transactional
    public void updateByPartNumber(Material material) {
        String updateQuery = "UPDATE material SET name = ?, part_number = ?, normalized_part_number = ?, hs_code = ? WHERE normalized_part_number = ?";
        jdbcTemplate.update(updateQuery, material.getName(), material.getPartNumber(), material.getNormalizedPartNumber(), material.getHsCode(), material.getNormalizedPartNumber());
    }

    /**
     * Returns all IDs from the input list that don't exist in the material table
     * @param ids List of integers to check
     * @return List of integers that don't have corresponding rows in material table
     */
    public List<Integer> findMissingIds(List<Integer> ids) {
        if (ids == null || ids.isEmpty()) {
            return List.of();
        }

        // Create placeholders for the IN clause
        String placeholders = ids.stream()
                .map(id -> "?")
                .collect(Collectors.joining(","));

        // Query to find existing IDs
        String sql = "SELECT id FROM material WHERE id IN (" + placeholders + ")";

        // Execute query and get existing IDs
        Set<Integer> existingIds = new HashSet<>(jdbcTemplate.queryForList(sql, Integer.class, ids.toArray()));

        // Return IDs that are in the input list but not in the database
        return ids.stream()
                .filter(id -> !existingIds.contains(id))
                .collect(Collectors.toList());
    }

    @Transactional
    public void insert(Material material) {
        String sql = """
        INSERT INTO material (part_number, normalized_part_number, hs_code, name, is_deprecated)
        VALUES (?, ?, ?, ?, ?)
        """;

        jdbcTemplate.update(sql,
                material.getPartNumber(),
                material.getNormalizedPartNumber(),
                material.getHsCode(),
                material.getName(),
                material.getDeprecated()
        );
    }

    public void deleteByIds(List<Integer> ids) {
        String placeholders = ids.stream()
                .map(id -> "?")
                .collect(Collectors.joining(","));

        String sql = "UPDATE material SET is_deprecated = TRUE WHERE id IN ("+placeholders+")";

        jdbcTemplate.update(sql, ids);
    }


    private static class MaterialMapper implements RowMapper<Material> {

        @Override
        public Material mapRow(ResultSet rs, int rowNum) throws SQLException {
            Material data = new Material();
            data.setId(rs.getInt("id"));
            data.setName(rs.getString("name"));
            data.setPartNumber(rs.getString("part_number"));
            data.setNormalizedPartNumber(rs.getString("normalized_part_number"));
            data.setHsCode(rs.getString("hs_code"));
            data.setDeprecated(rs.getBoolean("is_deprecated"));
            return data;
        }
    }

}
