package net.openright.simpleserverseed.domain.orders;

import net.openright.infrastructure.db.Database;
import net.openright.infrastructure.db.Database.Row;
import net.openright.infrastructure.rest.RequestException;
import net.openright.simpleserverseed.domain.products.ProductRepository;

import java.sql.SQLException;
import java.util.List;

class OrdersRepository {

	private final Database database;

	OrdersRepository(Database database) {
		this.database = database;
	}

	List<Order> list() {
		return database.queryForList("select * from orders order by title", this::toOrder);
	}

	Order retrieve(int id) {
		return database.queryForSingle("select * from orders where id = ?", (row) -> toOrderWithOrderLines(id, row), id)
				.orElseThrow(() -> new RequestException(404, "Can't find object with id " + id));
	}

	private Order toOrderWithOrderLines(int orderId, Row row) throws SQLException {
		Order order = toOrder(row);
		order.setOrderLines(queryForOrderLines(orderId));
		return order;
	}

	private List<OrderLine> queryForOrderLines(int orderId) {
		return database.queryForList("select * from order_lines INNER JOIN products on products.id = order_lines.product_id where order_id = ?",
				this::toOrderLine, orderId);
	}

	void insert(Order order) {
		validateOrder(order);

		database.doInTransaction(() -> {
			int orderId = database.insert("insert into orders (title) values (?) returning id", order.getTitle());
			order.setId(orderId);
			insertOrderLines(order.getId(), order);
		});
	}

	public void update(int orderId, Order order) {
		validateOrder(order);
		
		database.doInTransaction(() -> {
			updateOrder(orderId, order);
			deleteOrderLines(orderId);
			insertOrderLines(orderId, order);
		});
	}

	private void deleteOrderLines(int orderId) {
		database.executeOperation("delete from order_lines where order_id = ?", orderId);
	}

	private void updateOrder(int orderId, Order order) {
		database.executeOperation("update orders set title = ? where id = ?", order.getTitle(), orderId);
	}

	private void insertOrderLines(int orderId, Order order) {
		for (OrderLine orderLine : order.getOrderLines()) {
			database.executeOperation("insert into order_lines (amount, product_id, title, order_id) values (?, ?, ?, ?)",
					orderLine.getAmount(), orderLine.getProductId(), orderLine.getTitle(), orderId);
		}
	}

	private void validateOrder(Order order) {
		if (order.getTitle().equals("null")) {
			throw new RuntimeException("Null title is invalid");
		}
		if (order.getTitle().contains("foul")) {
			throw new RequestException("No foul language in orders, please!");
		}
	}

	private Order toOrder(Row row) throws SQLException {
		Order order = new Order(row.getString("title"));
		order.setId(row.getInt("id"));
		return order;
	}

	private OrderLine toOrderLine(Row row) throws SQLException {
		OrderLine orderLine = new OrderLine(row.getString("title"));
		orderLine.setProduct(ProductRepository.toProduct(row));
		orderLine.setAmount(row.getInt("amount"));
		return orderLine;
	}
}

@@ -616,32 +616,37 @@
 %09return database
+%0A%09%09%09%09
 .queryForSingle(
@@ -821,426 +821,8 @@
 %09%7D%0A%0A
-%09private Order toOrderWithOrderLines(int orderId, Row row) throws SQLException %7B%0A%09%09Order order = toOrder(row);%0A%09%09order.setOrderLines(queryForOrderLines(orderId));%0A%09%09return order;%0A%09%7D%0A%0A%09private List%3COrderLine%3E queryForOrderLines(int orderId) %7B%0A%09%09return database.queryForList(%22select * from order_lines INNER JOIN products on products.id = order_lines.product_id where order_id = ?%22,%0A%09%09%09%09this::toOrderLine, orderId);%0A%09%7D%0A%0A
 %09voi
@@ -1094,15 +1094,8 @@
 %7D%0A%0A%09
-public 
 void
@@ -1154,18 +1154,16 @@
 order);%0A
-%09%09
 %0A%09%09datab
@@ -1295,24 +1295,866 @@
 ;%0A%09%09%7D);%0A%09%7D%0A%0A
+%09private Order toOrder(Row row) throws SQLException %7B%0A%09%09Order order = new Order(row.getString(%22title%22));%0A%09%09order.setId(row.getInt(%22id%22));%0A%09%09return order;%0A%09%7D%0A%0A%09private OrderLine toOrderLine(Row row) throws SQLException %7B%0A%09%09OrderLine orderLine = new OrderLine(row.getString(%22title%22));%0A%09%09orderLine.setProduct(ProductRepository.toProduct(row));%0A%09%09orderLine.setAmount(row.getInt(%22amount%22));%0A%09%09return orderLine;%0A%09%7D%0A%0A%09private Order toOrderWithOrderLines(int orderId, Row row) throws SQLException %7B%0A%09%09Order order = toOrder(row);%0A%09%09order.setOrderLines(queryForOrderLines(orderId));%0A%09%09return order;%0A%09%7D%0A%0A%09private List%3COrderLine%3E queryForOrderLines(int orderId) %7B%0A%09%09return database%0A%09%09%09%09.queryForList(%0A%09%09%09%09%09%09%22select * from order_lines INNER JOIN products on products.id = order_lines.product_id where order_id = ?%22,%0A%09%09%09%09%09%09this::toOrderLine, orderId);%0A%09%7D%0A%0A
 %09private voi
@@ -2575,16 +2575,22 @@
 eration(
+%0A%09%09%09%09%09
 %22insert 
@@ -3027,417 +3027,7 @@
 %7D%0A%09%7D
-%0A%0A%09private Order toOrder(Row row) throws SQLException %7B%0A%09%09Order order = new Order(row.getString(%22title%22));%0A%09%09order.setId(row.getInt(%22id%22));%0A%09%09return order;%0A%09%7D%0A%0A%09private OrderLine toOrderLine(Row row) throws SQLException %7B%0A%09%09OrderLine orderLine = new OrderLine(row.getString(%22title%22));%0A%09%09orderLine.setProduct(ProductRepository.toProduct(row));%0A%09%09orderLine.setAmount(row.getInt(%22amount%22));%0A%09%09return orderLine;%0A%09%7D
 %0A%7D%0A

Reorder methods and format.