import com.gridnine.bof.midoffice.ibus.IBusMidofficeContextKeys
import com.gridnine.xtrip.common.ibecorp.model.InMobGdsAccount
import com.gridnine.xtrip.common.model.EntityReference
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.commission.ProductType
import com.gridnine.xtrip.common.model.booking.railway.RailwayProductIndex
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.helpers.SearchQueryHelper
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.util.MiscUtil
import com.gridnine.xtrip.common.util.TextUtil
import com.gridnine.xtrip.server.ibecorp.ibus.railway.im.IBusIMContextKeys
import com.gridnine.xtrip.server.ibecorp.ibus.railway.im.IMHelper
import com.gridnine.xtrip.server.ibecorp.ibus.railway.im.model.response.IMOrderListResp
import com.gridnine.xtrip.server.ibecorp.ibus.railway.im.orderList.GetOrderListParameters
import com.gridnine.xtrip.server.ibus.IntegrationBusFacade
import groovy.transform.Field

import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.stream.Collectors

@Field final Date startDate = parameters['key-report-params']['periodBegin'] as Date
@Field final Date endDate = parameters['key-report-params']['periodEnd'] as Date
@Field final EntityReference<InMobGdsAccount> gdsAccountReference = parameters['GDS_ACCOUNT'] as EntityReference<InMobGdsAccount>
@Field final EntityReference<Organization> agencyRef = parameters['AGENCY'] as EntityReference<Organization>
@Field final Boolean saveTracingDocuments = parameters['SAVE_TRACING_DOCUMENTS'] as Boolean

//CREATING STYLES
createStyle(name: 'title',fontBold: true, h_span: 13, h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:20)
createStyle(name: 'header',fontBold: false,  h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:10, leftBorder:'MEDIUM', rightBorder:'MEDIUM', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
createStyle(name: 'data',fontBold: false,  h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:10, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN')
createStyle(name: 'textData',parent: 'data')
createStyle(name: 'numberData',parent: 'data',h_alignment: 'RIGHT')
createStyle(name: 'dateData',parent: 'data',h_alignment: 'RIGHT',format: 'm/d/yy')
createStyle(name: 'subtotalTitle', parent: 'header', h_span: 6, h_alignment: 'RIGHT', fontBold: true)
createStyle(name: 'subtotalNumber',parent: 'subtotalTitle',  h_span: 1)
createStyle(name: 'totalTitle', parent: 'subtotalTitle', h_span: 3)
createStyle(name: 'totalNumber',parent: 'totalTitle')

//REPORT
page{"Сравнение"} {
    // Set portrait mode
    landscape(true)

    // Set narrow margins
    margin(0.25, 0.25, 0.75, 0.75)

    // Set scale
    scale(90)

    //TITLE
    rowHeight(30)
    setStyle('title')
    text("Сравнение данных за период ${parameters.REPORT_PERIOD}")
    if (agencyRef != null) {
        nextRow()
        text("Агентство: ${agencyRef}")
    }

    //HEADER
    setStyle('normal')
    nextRow()

    rowHeight(15)
    nextRow()

    rowHeight(40)
    setStyle('header')
    text('Источник'); nextColumn()
    text('Номер бронирования'); nextColumn()
    text('Номер билета'); nextColumn()
    text('Тип продукта'); nextColumn()
    text('Статус'); nextColumn()
    text('Дата'); nextColumn()
    text('Тариф'); nextColumn()
    nextRow()

    def wrappers = getResultWrappers()

    def dateFormat = new SimpleDateFormat("dd.MM.yyyy")

    wrappers.each { RailWrapper wrapper ->
        rowHeight(12)
        text(wrapper.source,'textData'); nextColumn()
        text(wrapper.reservationNumber,'textData'); nextColumn()
        text(wrapper.ticketNumber,'textData'); nextColumn()
        text(wrapper.productType?.toString(),'textData'); nextColumn()
        text(wrapper.status?.toString(),'textData'); nextColumn()
        text(formatDate(wrapper.operationDate, dateFormat),'textData'); nextColumn()
        number(wrapper.total,'numberData'); nextColumn()
        nextRow()
    }

    setAutoWidth()
}

List<RailWrapper> getResultWrappers() {
    List<RailWrapper> productWrappers = getProductWrappers()
    List<RailWrapper> imWrappers = getImWrappers()

    List<RailWrapper> resultWrappers = []

    imWrappers.each { RailWrapper imWrapper ->
        RailWrapper foundProductWrapper = productWrappers.find { RailWrapper productWrapper ->
            if (imWrapper.status != productWrapper.status) {
                return false
            }
            if (TextUtil.nonBlank(productWrapper.ticketNumber)) {
                if (imWrapper.ticketNumber == productWrapper.ticketNumber) {
                    return true
                }
            } else {
                if (MiscUtil.isSameDay(imWrapper.operationDate, productWrapper.operationDate)
                        && MiscUtil.equals(imWrapper.total, productWrapper.total)) {
                    return true
                }
            }
            return false
        }

        if (foundProductWrapper == null) {
            resultWrappers << imWrapper
            return
        }

        productWrappers.remove(foundProductWrapper)

        RailWrapper item1 = new RailWrapper()
        item1.source = imWrapper.source
        item1.status = imWrapper.status
        item1.reservationNumber = imWrapper.reservationNumber
        item1.ticketNumber = imWrapper.ticketNumber

        RailWrapper item2 = new RailWrapper()
        item2.source = foundProductWrapper.source
        item2.status = foundProductWrapper.status
        item2.reservationNumber = foundProductWrapper.reservationNumber
        item2.ticketNumber = foundProductWrapper.ticketNumber
        item2.productType = foundProductWrapper.productType

        boolean hasDiff = false

        if (!equalsByDate(imWrapper, foundProductWrapper)) {
            hasDiff = true
            item1.operationDate = imWrapper.operationDate
            item2.operationDate = foundProductWrapper.operationDate
        }
        if (!equalsByTariff(imWrapper, foundProductWrapper)) {
            hasDiff = true
            item1.total = imWrapper.total
            item2.total = foundProductWrapper.total
        }

        if (hasDiff) {
            resultWrappers << item1
            resultWrappers << item2
        }
    }

    resultWrappers.addAll(productWrappers)

    def dateFormat = new SimpleDateFormat("dd.MM.yyyy")
    resultWrappers.sort { RailWrapper wrapper ->
        formatDate(wrapper?.operationDate, dateFormat) +
                wrapper?.ticketNumber + wrapper?.source + wrapper?.status
    }
}

static boolean equalsByDate(RailWrapper t1, RailWrapper t2) {
    return MiscUtil.isSameDay(t1.operationDate, t2.operationDate)
}

static boolean equalsByTariff(RailWrapper t1, RailWrapper t2) {
    return MiscUtil.equals(notNullNumber(t1.total), notNullNumber(t2.total))
}

static BigDecimal notNullNumber(BigDecimal value) {
    return value ?: BigDecimal.ZERO
}

static formatDate(Date date, DateFormat dateFormat) {
    return date ? dateFormat.format(date) : null
}

List<RailWrapper> getProductWrappers() {
    List<RailWrapper> productWrappers = []

    tickets { RailwayProductIndex index ->
        def wrapper = new RailWrapper()

        wrapper.status = index.status
        if (!index.recordLocators.isEmpty()) {
            wrapper.reservationNumber = SearchQueryHelper.getRecordLocatorsFromIndexRecords(index.recordLocators).stream().collect(Collectors.joining(", "))
        } else {
            wrapper.reservationNumber = index.recordLocator
        }
        wrapper.ticketNumber = index.systemNumber
        wrapper.productType = index.productType
        wrapper.operationDate = index.issueDateTime
        wrapper.total = index.equivalentTotalFare
        wrapper.source = "AGENCY"

        productWrappers << wrapper
    }

    return productWrappers
}

List<RailWrapper> getImWrappers() {
    def gdsAccount = EntityStorage.get().resolve(gdsAccountReference)
    List<RailWrapper> imWrappers = []

    int days = MiscUtil.getDaysBetween(startDate, endDate)
    if (days < 0) {
        return
    }
    for (int delta = 0; delta < days + 1; delta++) {
        Map<String, Object> context = new HashMap<>()
        context.put(IBusIMContextKeys.IM_ORDER_LIST_PARAMETERS.name(), buildGetOrderListParams(MiscUtil.addDaysToDate(startDate, delta)))
        context.put(IBusMidofficeContextKeys.GDS_ACCOUNT.name(), gdsAccount)
        context.put(IBusIMContextKeys.IM_SAVE_TRACING_DOCUMENTS.name(), saveTracingDocuments)

        IntegrationBusFacade.get().processRouteSync("railway:im:order-list:order-list-route", context)

        IMOrderListResp response = (IMOrderListResp) context.get(IBusIMContextKeys.IM_RESPONSE_DATA.name());

        response.orders.each { IMOrderListResp.Order order ->
            order.orderItems.each {IMOrderListResp.ShortOrderItem orderItem ->
                ProductStatus status
                if ("Purchase".equalsIgnoreCase(orderItem.operationType)){
                    status = ProductStatus.SELL
                } else if ("Return".equalsIgnoreCase(orderItem.operationType)){
                    status = ProductStatus.REFUND
                }
                String orderItemId = (status == ProductStatus.REFUND) ? orderItem.previousOrderItemId : orderItem.orderItemId
                String pnr = orderItem.orderId + "/" + orderItemId
                Date operationDate = IMHelper.formatZonedDate(orderItem.confirmDateTime)

                orderItem.orderItemBlanks.each {blank ->
                    def wrapper = new RailWrapper()

                    wrapper.status = status
                    wrapper.reservationNumber = pnr
                    wrapper.ticketNumber = blank.blankNumber
                    wrapper.operationDate = operationDate
                    wrapper.total = (status == ProductStatus.REFUND) ? MiscUtil.negate(blank.amount) : blank.amount
                    wrapper.source = "IM"

                    imWrappers << wrapper
                }
            }
        }
    }
    return imWrappers
}

GetOrderListParameters buildGetOrderListParams(Date date) {
    GetOrderListParameters result = new GetOrderListParameters();
    result.setDate(date);
    result.setOnlyRefund(false);
    result.setOnlyPaymentsByCard(false);
    result.setOnlyExternal(false);
    return result;
}

def setAutoWidth() {
    nextRow()
    rowAutoHeight()

    6.times {
        nextColumn()
        columnAutoWidth()
    }
}

class RailWrapper {
    ProductStatus status
    String reservationNumber
    String ticketNumber
    ProductType productType
    Date operationDate
    BigDecimal total
    String source
}