package de.avatic.lcc.repositories.packaging;

import de.avatic.lcc.model.db.packaging.Packaging;
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Repository
public class PackagingRepository {


    public List<Packaging> listAllPackaging() {
        String query = "SELECT * FROM packaging ORDER BY id";
        return jdbcTemplate.query(query, new PackagingMapper());
    }

    private static class PackagingMapper implements RowMapper<Packaging> {

        @Override
        public Packaging mapRow(ResultSet rs, int rowNum) throws SQLException {
            Packaging data = new Packaging();
            data.setId(rs.getInt("id"));
            data.setHuId(rs.getInt("hu_dimension_id"));
            data.setShuId(rs.getInt("shu_dimension_id"));
            data.setMaterialId(rs.getInt("material_id"));
            data.setSupplierId(rs.getInt("supplier_node_id"));
            data.setDeprecated(rs.getBoolean("is_deprecated"));
            return data;
        }
    }


    private final JdbcTemplate jdbcTemplate;

    public PackagingRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Transactional
    public SearchQueryResult<Packaging> listPackaging(Integer materialId, Integer supplierId, boolean excludeDeprecated, SearchQueryPagination pagination) {

        String query = buildQuery(materialId, supplierId, excludeDeprecated);

        var params = new ArrayList<Object>();
        params.add(excludeDeprecated);
        if (materialId != null) {
            params.add(materialId);
        }
        if (supplierId != null) {
            params.add(supplierId);
        }
        params.add(pagination.getLimit());
        params.add(pagination.getOffset());

        var packaging = jdbcTemplate.query(query, new PackagingMapper(), params.toArray());

        return new SearchQueryResult<>(packaging, pagination.getPage(), countPackaging(materialId, supplierId, excludeDeprecated), pagination.getLimit());
    }

    private static String buildQuery(Integer materialId, Integer supplierId, boolean excludeDeprecated) {
        StringBuilder queryBuilder = new StringBuilder("""
                SELECT id,
                FROM packaging
                WHERE 1=1""");

        if (excludeDeprecated) {
            queryBuilder.append(" AND is_deprecated = FALSE");
        }
        if (materialId != null) {
            queryBuilder.append(" AND material_id = ?");
        }
        if (supplierId != null) {
            queryBuilder.append(" AND supplier_node_id = ?");
        }
        queryBuilder.append("ORDER BY id LIMIT ? OFFSET ?");
        return queryBuilder.toString();
    }

    private Integer countPackaging(Integer materialId, Integer supplierId, boolean excludeDeprecated) {
        StringBuilder queryBuilder = new StringBuilder("""
                SELECT COUNT(*)
                FROM packaging
                WHERE packaging.is_deprecated = ?""");

        if (materialId != null) {
            queryBuilder.append(" AND material_id = ?");
        }
        if (supplierId != null) {
            queryBuilder.append(" AND supplier_node_id = ?");
        }

        var params = new ArrayList<Object>();
        params.add(excludeDeprecated);
        if (materialId != null) {
            params.add(materialId);
        }
        if (supplierId != null) {
            params.add(supplierId);
        }

        return jdbcTemplate.queryForObject(queryBuilder.toString(), Integer.class, params.toArray());
    }


    @Transactional
    public Optional<Packaging> getById(Integer id) {
        String query = """
                SELECT id, supplier_node_id, material_id, hu_dimension_id, shu_dimension_id, is_deprecated
                FROM packaging
                WHERE packaging.id = ?""";

        var entity = jdbcTemplate.queryForObject(query, new PackagingMapper(), id);

        return Optional.ofNullable(entity);
    }

    @Transactional
    public void update(Packaging packaging) {
        String query = "UPDATE packaging SET is_deprecated = ?, supplier_node_id = ?, material_id = ?, hu_dimension_id = ?, shu_dimension_id = ? WHERE id = ?";

        jdbcTemplate.update(query,
                packaging.getDeprecated(),
                packaging.getSupplierId(),
                packaging.getMaterialId(),
                packaging.getHuId(),
                packaging.getShuId(),
                packaging.getId());

    }


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

    @Transactional
    public Optional<Integer> insert(Packaging entity) {

        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(connection -> {
            PreparedStatement ps = connection
                    .prepareStatement("INSERT INTO packaging (supplier_node_id, material_id, hu_dimension_id, shu_dimension_id, is_deprecated) VALUES (?, ?, ?, ?, ?)",
                            Statement.RETURN_GENERATED_KEYS);
            ps.setInt(1, entity.getSupplierId());  //
            ps.setInt(2, entity.getMaterialId());
            ps.setInt(3, entity.getHuId());
            ps.setInt(4, entity.getShuId());
            ps.setBoolean(5, entity.getDeprecated());
            return ps;
        }, keyHolder);


        return Optional.ofNullable(!Objects.requireNonNull(keyHolder.getKeys()).isEmpty() ? ((Integer) keyHolder.getKeys().values().iterator().next())
                : null);

    }

    public List<Packaging> getByMaterialId(Integer materialId) {
        String query = """
                SELECT id, supplier_node_id, material_id, hu_dimension_id, shu_dimension_id, is_deprecated
                FROM packaging
                WHERE packaging.material_id = ?""";

        return jdbcTemplate.query(query, new PackagingMapper(), materialId);
    }

    public Optional<Packaging> getByMaterialIdAndSupplierId(Integer materialId, Integer supplierId) {
        String query = """
                SELECT id, supplier_node_id, material_id, hu_dimension_id, shu_dimension_id, is_deprecated
                FROM packaging
                WHERE packaging.material_id = ? AND packaging.supplier_node_id = ?""";

        var packaging = jdbcTemplate.query(query, new PackagingMapper(), materialId, supplierId);

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

        if(packaging.size() > 1)
            throw new IllegalStateException("Multiple packaging found for materialId: " + materialId + " and supplierId: " + supplierId);

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