package com.capgemini.reports.reports.parameters;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.queries.saved.modelobject.ItemQuery;
import com.capgemini.reports.exceptions.NotFoundException;
import com.teamcenter.soa.client.model.strong.Item;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.List;

/**
 * This class gathers information about a product (+unit), and optionally a generic or a generic assemb.<br>
 * This class allows either: <br>
 * <ul>
 * <li>A product and its unit</li>
 * <li>A product, its unit, and a Generic that exists under this product</li>
 * <li>A product, its unit, and a GenericAssemb that exists under this product</li>
 * </ul>
 */
@Slf4j
@Getter
@EqualsAndHashCode
public class Generics implements Serializable {

    private static final long serialVersionUID = 2362411651537951479L;
    private static final String GENERIC_BOM = "GenericBom";
    private static final String ASSEMBLY_BOM = "AssemblyBom";
    private static final String TECHNICAL_BOM = "TechnicalBom";
    private final String generic;
    private final String genericAssemb;
    private final String revision;
    private final ProductUnit productUnit;
    private final GenericType type;


    /**
     * Constructs a {@link Generics}.
     *
     * @param productUnit     a {@link ProductUnit} (mandatory)
     * @param genericID       (may be null)
     * @param genericAssembID (may be null)
     */
    public Generics(final ProductUnit productUnit, final String genericID, final String genericAssembID) {
        super();
        this.productUnit = productUnit;
        if (genericID != null && !genericID.isEmpty()) {
            this.type = GenericType.GENERIC;
            this.generic = genericID;
            this.genericAssemb = null;
        } else {
            if (genericAssembID != null && !genericAssembID.isEmpty()) {
                this.type = GenericType.GENERIC_ASSEMB;
                this.generic = null;
                this.genericAssemb = genericAssembID;
            } else {
                this.type = GenericType.ONLY_PRODUCT;
                this.generic = null;
                this.genericAssemb = null;
            }
        }
        // The parameter revision is not used via this constructor.
        this.revision = null;
    }

    /**
     * Constructs a {@link Generics} Revision.
     *
     * @param productUnit     a {@link ProductUnit} (mandatory)
     * @param genericID       (may be null)
     * @param genericAssembID (may be null)
     * @param genericRevision (may be null) the revision of the Generic
     */
    public Generics(final ProductUnit productUnit, final String genericID, final String genericAssembID, final String genericRevision) {
        super();
        this.productUnit = productUnit;
        if (genericID != null && !genericID.isEmpty()) {
            this.type = GenericType.GENERIC;
            this.generic = genericID;
            this.genericAssemb = null;
        } else {
            if (genericAssembID != null && !genericAssembID.isEmpty()) {
                this.type = GenericType.GENERIC_ASSEMB;
                this.generic = null;
                this.genericAssemb = genericAssembID;
            } else {
                this.type = GenericType.ONLY_PRODUCT;
                this.generic = null;
                this.genericAssemb = null;
            }
        }
        this.revision = genericRevision;
    }

    /**
     * Returns the corresponding Teamcenter {@link Item} (a product, Generic or Generic Assemb).
     *
     * @param session the {@link TcSession} to perform the query.
     * @return the {@link Item} representing the item.
     * @throws NotFoundException if no reults were found
     */
    public Item getItem(final TcSession session) throws NotFoundException {
        ItemQuery savedQuery = new ItemQuery(session);
        switch (this.type) {
            case GENERIC:
                savedQuery.searchID(generic);
                break;
            case GENERIC_ASSEMB:
                savedQuery.searchID(genericAssemb);
                break;
            case ONLY_PRODUCT:
                savedQuery.searchID(productUnit.getProductID());
                break;
            default:
                String msg = "Undefined Generics state: cannot obtain the ItemRevision";
                log.error(msg);
                throw new NotFoundException(msg);
        }
        List<Item> results = savedQuery.findAll();
        if (results == null || results.isEmpty()) {
            throw new NotFoundException("No Generic found with the provided ID");
        }
        return results.get(0);
    }

    /**
     * Returns the corresponding item ID (of the product, Generic or Generic Assemb).
     *
     * @return the item ID
     */
    public String getID() {
        String returnStr;
        switch (this.type) {
            case GENERIC:
                returnStr = generic;
                break;
            case GENERIC_ASSEMB:
                returnStr = genericAssemb;
                break;
            case ONLY_PRODUCT:
                returnStr = productUnit.getProductID();
                break;
            default:
                log.warn("Undefined Generics state: cannot select an itemID");
                returnStr = "";
                break;
        }
        return returnStr;
    }

    @Override
    public String toString() {
        String returnStr;
        switch (type) {
            case GENERIC:
                returnStr = "Generic: " + generic;
                break;
            case GENERIC_ASSEMB:
                returnStr = "Generic Assemb: " + genericAssemb;
                break;
            case ONLY_PRODUCT:
                returnStr = "No Generics selected, using father Product: " + productUnit.getProductID();
                break;
            default:
                returnStr = "Undefined Generics state: defaulting to father Product: " + productUnit.getProductID();
                break;
        }
        return returnStr;
    }

    /**
     * Represents a Product-Unit, and some optional Generic or GenericAssemb under it.
     */
    @Getter
    public enum GenericType {
        /**
         * Represents only a product-unit. Follows a BomView named "<b>TechnicalBom</b>".
         */
        ONLY_PRODUCT(TECHNICAL_BOM),
        /**
         * Represents a product-unit and a generic under it. Follows a BomView named "<b>GenericBOM</b>".
         */
        GENERIC(GENERIC_BOM),
        /**
         * Represents a product-unit and a genericAssemb under it. Follows a BomView named "<b>AssemblyBOM</b>".
         */
        GENERIC_ASSEMB(ASSEMBLY_BOM);

        private final String bomLinkType;

        GenericType(final String bomLinkType) {
            this.bomLinkType = bomLinkType;
        }
    }
}
