import com.gridnine.xtrip.common.model.EntityContainer
import com.gridnine.xtrip.common.model.Xeption
import com.gridnine.xtrip.common.model.booking.BaseProduct
import com.gridnine.xtrip.common.model.booking.agencymemo.AgencyMemoProduct
import com.gridnine.xtrip.common.model.booking.agencymemo.AgencyMemoProductType
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.ServiceType
import com.gridnine.xtrip.common.model.dict.ContractType
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.entity.EntityStorageHelper
import com.gridnine.xtrip.common.model.finance.Shipment
import com.gridnine.xtrip.common.model.helpers.GeneralProductHelper
import com.gridnine.xtrip.common.model.helpers.ProductStatusHelper
import com.gridnine.xtrip.common.util.MiscUtil

import java.util.stream.Collectors

class ShipmentReconciliationItem {
    String passengerName
    String ticketNumber
    BigDecimal shipmentServiceSum=BigDecimal.ZERO
    BigDecimal shipmentFeesSum=BigDecimal.ZERO
    BigDecimal shipmentTotal=BigDecimal.ZERO
    BigDecimal productServiceSum=BigDecimal.ZERO
    BigDecimal productFeesSum=BigDecimal.ZERO
    BigDecimal productTotal=BigDecimal.ZERO
    boolean differs
}
def getFeesCollection = { BaseProduct prod, Shipment shipment ->
    def contractType = ContractType.CLIENT
    def contractCtr = EntityStorage.get().resolve(shipment.contract)
    if(contractCtr != null && contractCtr.entity.contractType == ContractType.SUBAGENCY){
        contractType = ContractType.SUBAGENCY
    }
    def relation = GeneralProductHelper.getContractRelation(prod, contractType)
    if(relation == null){
        return Collections.emptyList()
    }
    return GeneralProductHelper.filterCommissions(relation.getCommissions(), GeneralProductHelper.feePropertyTypes, null, null)
}

def getProductPriceInt = { BaseProduct prod, Shipment shipment ->
    def handler = GeneralProductHelper.getHandler(prod)
    BigDecimal result = MiscUtil.guarded((BigDecimal) handler.getEquivalentFare(prod))
    handler.getTaxes(prod).forEach{
        result = result.add(MiscUtil.guarded((BigDecimal) it.equivalentAmount))
    }
    handler.getProductEquivalentAdditionalServices(prod)?.forEach{
        result = MiscUtil.sum(result, it.amount?.value)
    }
    def fees = getFeesCollection(prod, shipment)
    fees.forEach{
        if(GeneralProductHelper.isHiddenFee(it)){
            result = result.add(MiscUtil.guarded(it.equivalentAmount))
        }
    }
    ProductStatus status = handler.getStatus(prod)
    if(ProductStatusHelper.get().negatesPrice(status) ||
            (prod instanceof AgencyMemoProduct && AgencyMemoProductType.ACM == ((AgencyMemoProduct) prod).getType())) {
        result = result.negate()
    }
    result =  result.add(MiscUtil.guarded(handler.getPenalty(prod) as BigDecimal))
    return result
}
def getFeesInt = { BaseProduct prod, Shipment shipment ->
    def handler = GeneralProductHelper.getHandler(prod)
    def fees = getFeesCollection(prod, shipment)
    def result = BigDecimal.ZERO
    fees.forEach{
        if(!GeneralProductHelper.isHiddenFee(it)){
            result = result.add(MiscUtil.guarded(it.equivalentAmount))
        }
    }
    ProductStatus status = handler.getStatus(prod)
    if(ProductStatusHelper.get().negatesPrice(status) ||
            (prod instanceof AgencyMemoProduct && AgencyMemoProductType.ACM == ((AgencyMemoProduct) prod).getType())) {
        result = result.negate()
    }
    return result
}

def getFeesAmount = {BaseProduct prod, Shipment shipment ->
    def handler = GeneralProductHelper.getHandler(prod)
    BigDecimal result = getFeesInt(prod, shipment)
    if(handler.getPreviousProduct(prod) != null && handler.getStatus(handler.getPreviousProduct(prod)) == ProductStatus.EXCHANGE){
        result = result.add(getFeesInt(handler.getPreviousProduct(prod), shipment))
    }
    return result
}

def getProductPrice = {BaseProduct prod, Shipment shipment ->
    def handler = GeneralProductHelper.getHandler(prod)
    BigDecimal result = getProductPriceInt(prod, shipment)
    if(handler.getPreviousProduct(prod) != null && handler.getStatus(handler.getPreviousProduct(prod)) == ProductStatus.EXCHANGE){
        result = result.add(getProductPriceInt(handler.getPreviousProduct(prod), shipment))
    }
    return result
}



EntityContainer<Shipment> shipmentCtr = EntityStorage.get().resolve(parameters['shipment'])
if(shipmentCtr == null){
    throw Xeption.forEndUser("не удалось загрузить реализацию")
}
def ctx = EntityStorageHelper.createContext(shipmentCtr)
def productsMap = new HashMap<String, ShipmentReconciliationItem>()
shipmentCtr.entity.getBillingItems().forEach{ biRef ->
    def billingItem = EntityStorageHelper.resolve(biRef, ctx)
    if(billingItem != null){
        def product = EntityStorageHelper.resolve(billingItem.product, ctx)
        if(product != null){
            def item = productsMap[product.uid]
            if(item == null){
                item = new ShipmentReconciliationItem()
                productsMap[product.uid] = item
                item.ticketNumber = GeneralProductHelper.getSystemNumber(product)
                item.passengerName = GeneralProductHelper.getTravellers(product).stream().map{it.name}.collect(Collectors.toList()).join("/")
                item.productServiceSum = getProductPrice(product, shipmentCtr.entity)
                item.productFeesSum = getFeesAmount(product, shipmentCtr.entity)
            }
            if(billingItem.serviceType == ServiceType.TICKET || billingItem.serviceType == ServiceType.PENALTY ){
                item.shipmentServiceSum = MiscUtil.sum(item.shipmentServiceSum, billingItem.amount.getTotal())
            } else {
                item.shipmentFeesSum = MiscUtil.sum(item.shipmentFeesSum, billingItem.amount.getTotal())
            }
        }
    }
}
productsMap.values().forEach{ item ->
    item.differs = !MiscUtil.isSame(item.shipmentServiceSum, item.productServiceSum) || !MiscUtil.isSame(item.shipmentFeesSum, item.productFeesSum)
}

def items = new ArrayList<ShipmentReconciliationItem>()
items.addAll(productsMap.values())
items.sort{it.passengerName}
// Styles
createStyle(name: 'title', h_alignment: 'CENTER', fontHeight: 14, v_alignment: 'CENTER')
createStyle(name: "header", h_alignment: "CENTER", v_alignment: "CENTER", fontBold : true, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: 'textData', h_alignment: 'CENTER', v_alignment: 'CENTER', leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN')
createStyle(name: 'textDataDiff', parent: "textData", foreground:"YELLOW")
createStyle(name: 'numberData', h_alignment: 'RIGHT', v_alignment: 'CENTER', leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN')
createStyle(name: 'numberDataDiff', parent: "numberData", foreground:"YELLOW")
createStyle(name: 'totalTextData', parent:'textData',  fontBold: true)
createStyle(name: 'totalTitle', parent:'totalTextData',   h_alignment: "RIGHT")
createStyle(name: 'totalNumberData', parent:'numberData',  fontBold: true)


page{'Сверка'}{
    rowHeight(20)
    text('Отчет сверки по реализации ' + parameters['shipment'], 'title', 8, 1)
    nextRow()
    nextRow()
    rowHeight(40)
    text("ФИО пассажира", "header")
    columnWidth(35)
    nextColumn()
    text("Номер билета", "header")
    columnWidth(20)
    nextColumn()
    text("Услуга (реализация)", "header")
    columnWidth(15)
    nextColumn()
    text("Сборы (реализация)", "header")
    columnWidth(15)
    nextColumn()
    text("Итого (реализация)", "header")
    columnWidth(15)
    nextColumn()
    text("Услуга (продукт)", "header")
    columnWidth(15)
    nextColumn()
    text("Сборы (продукт)", "header")
    columnWidth(15)
    nextColumn()
    text("Итого (продукт)", "header")
    columnWidth(15)
    items.forEach{ item ->
        nextRow()
        rowHeight(15)
        text(item.passengerName, item.differs? 'textDataDiff': 'textData')
        nextColumn()
        text(item.ticketNumber, item.differs? 'textDataDiff': 'textData')
        nextColumn()
        number(item.shipmentServiceSum, item.differs? 'numberDataDiff': 'numberData')
        nextColumn()
        number(item.shipmentFeesSum, item.differs? 'numberDataDiff': 'numberData')
        nextColumn()
        formula("${cellIndex(0, -2)}+${cellIndex(0, -1)}", item.differs? 'numberDataDiff': 'numberData')
        nextColumn()
        number(item.productServiceSum, item.differs? 'numberDataDiff': 'numberData')
        nextColumn()
        number(item.productFeesSum, item.differs? 'numberDataDiff': 'numberData')
        nextColumn()
        formula("${cellIndex(0, -2)}+${cellIndex(0, -1)}", item.differs? 'numberDataDiff': 'numberData')
    }
    def itemsSize =items.size()
    if(itemsSize > 0){
        nextRow()
        rowHeight(20)
        text("Итого","totalTitle", 2, 1)
        nextColumn()
        nextColumn()
        formula("SUM(${cellIndex(-itemsSize, 0)}:${cellIndex(-1, 0)})",  'totalNumberData')
        nextColumn()
        formula("SUM(${cellIndex(-itemsSize, 0)}:${cellIndex(-1, 0)})",  'totalNumberData')
        nextColumn()
        formula("SUM(${cellIndex(-items.size(), 0)}:${cellIndex(-1, 0)})",  'totalNumberData')
        nextColumn()
        formula("SUM(${cellIndex(-items.size(), 0)}:${cellIndex(-1, 0)})",  'totalNumberData')
        nextColumn()
        formula("SUM(${cellIndex(-items.size(), 0)}:${cellIndex(-1, 0)})",  'totalNumberData')
        nextColumn()
        formula("SUM(${cellIndex(-items.size(), 0)}:${cellIndex(-1, 0)})",  'totalNumberData')
    }
}
