package org.hso.ecommerce.action.warehouse;

import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import org.hso.ecommerce.entities.warehouse.WarehouseBooking;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class SupplierOrderArrivedAction {

    private final Article article;
    private final ArrayList<WarehouseBookingPositionSlotEntry> warehouseCandidates;
    private final SupplierOrder order;

    public SupplierOrderArrivedAction(List<WarehouseBookingPositionSlotEntry> warehouseCandidates, SupplierOrder order, Article article) {
        this.warehouseCandidates = new ArrayList<>(warehouseCandidates);
        this.order = order;
        this.article = article;
    }

    public Result finish() throws NoSpaceInWarehouseException {
        // Sort for most filled slot first;
        warehouseCandidates.sort((b, a) -> Integer.compare(a.newSumSlot, b.newSumSlot));

        int needed = order.numberOfUnits;

        WarehouseBooking booking = new WarehouseBooking();
        booking.created = new Timestamp(new Date().getTime());
        booking.reason = new BookingReason(order);

        for (WarehouseBookingPositionSlotEntry entry : warehouseCandidates) {
            int canBeAdded = article.warehouseUnitsPerSlot - entry.newSumSlot;

            if (canBeAdded == 0) {
                // this slot is full, skip
                continue;
            }

            int willBeAdded = Math.min(canBeAdded, needed);
            needed -= willBeAdded;

            WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition();

            bookingPosition.article = article;
            bookingPosition.amount = willBeAdded;
            bookingPosition.slotEntry = entry.copyAddAmount(willBeAdded, article);
            bookingPosition.booking = booking;

            booking.positions.add(bookingPosition);

            if (needed == 0) {
                break;
            }
        }

        if (needed > 0) {
            throw new NoSpaceInWarehouseException(article);
        }

        order.delivered = new Timestamp(new Date().getTime());
        return new Result(order, booking);
    }

    public static class Result extends Exception {

        private final SupplierOrder order;
        private final WarehouseBooking booking;

        public Result(SupplierOrder order, WarehouseBooking booking) {
            this.order = order;
            this.booking = booking;
        }

        public SupplierOrder getOrder() {
            return order;
        }

        public WarehouseBooking getBooking() {
            return booking;
        }
    }


    public static class NoSpaceInWarehouseException extends Exception {
        private Article article;

        public NoSpaceInWarehouseException(Article article) {
            super("The quantity of article '" + article.title + "' does not fit in warehouse.");
            this.article = article;
        }

        public Article getArticle() {
            return article;
        }
    }

}
