import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.railway.RailwayProductIndex
import com.gridnine.xtrip.common.model.helpers.SearchQueryHelper
import com.gridnine.xtrip.common.util.MiscUtil
import com.gridnine.xtrip.common.util.TextUtil
import com.gridnine.xtrip.server.ibecorp.ibus.railway.ufs.IBusUfsContextKeys
import com.gridnine.xtrip.server.ibecorp.ufs.model.TransInfoResponse
import com.gridnine.xtrip.server.ibecorp.ufs.model.domain.BlankInfo
import com.gridnine.xtrip.server.ibecorp.ufs.model.domain.TransactionType
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

//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}")

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

    rowHeight(15)
    nextRow()

    rowHeight(40)
    setStyle('header')
    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 { UfsWrapper wrapper ->
        rowHeight(12)
        text(wrapper.source,'textData'); nextColumn()
        text(wrapper.reservationNumber,'textData'); nextColumn()
        text(wrapper.ticketNumber,'textData'); nextColumn()
        text(wrapper.status?.toString(),'textData'); nextColumn()
        text(formatDate(wrapper.operationDate, dateFormat),'textData'); nextColumn()
        number(wrapper.total,'numberData'); nextColumn()
        nextRow()
    }

    setAutoWidth()
}

List<UfsWrapper> getResultWrappers() {
    List<UfsWrapper> productWrappers = getProductWrappers()
    List<UfsWrapper> ufsWrappers = getUfsWrappers()

    List<UfsWrapper> resultWrappers = []

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

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

        productWrappers.remove(foundProductWrapper)

        UfsWrapper item1 = new UfsWrapper()
        item1.source = ufsWrapper.source
        item1.status = ufsWrapper.status
        item1.reservationNumber = ufsWrapper.reservationNumber
        item1.ticketNumber = ufsWrapper.ticketNumber

        UfsWrapper item2 = new UfsWrapper()
        item2.source = foundProductWrapper.source
        item2.status = foundProductWrapper.status
        item2.reservationNumber = foundProductWrapper.reservationNumber
        item2.ticketNumber = foundProductWrapper.ticketNumber

        boolean hasDiff = false

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

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

    resultWrappers.addAll(productWrappers)

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

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

static boolean equalsByTariff(UfsWrapper t1, UfsWrapper 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<UfsWrapper> getProductWrappers() {
    List<UfsWrapper> productWrappers = []

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

        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.operationDate = index.issueDateTime
        wrapper.total = index.equivalentTotalFare
        wrapper.source = "AGENCY"

        productWrappers << wrapper
    }

    return productWrappers
}

List<UfsWrapper> getUfsWrappers() {
    Map<String, Object> context = new HashMap<>()
    context.put(IBusUfsContextKeys.UFS_START_DATE.name(), startDate)
    context.put(IBusUfsContextKeys.UFS_END_DATE.name(), endDate)

    IntegrationBusFacade.get().processRouteSync("railway:ufs:get-trans-list:get-trans-info-list", context)

    def ufsTransactions = (List<TransInfoResponse>) context.get(IBusUfsContextKeys.UFS_TRANS_INFO_LIST.name())

    List<UfsWrapper> ufsWrappers = []

    ufsTransactions.each { TransInfoResponse transInfoResponse ->
        def transInfo = transInfoResponse?.transInfo
        if (transInfoResponse.transInfo?.tStatus == 0) {
            transInfoResponse.blanks.each { BlankInfo blankInfo ->
                def wrapper = new UfsWrapper()

                ProductStatus status
                if (transInfo?.transactionType == TransactionType.PURCHASE) {
                    status = ProductStatus.SELL
                } else if (transInfo?.transactionType == TransactionType.REFUND) {
                    status = ProductStatus.REFUND
                } else {
                    status = ProductStatus.VOID
                }

                wrapper.status = status
                wrapper.reservationNumber = (status == ProductStatus.REFUND) ? transInfo?.prevTrans : transInfo?.idTrans
                wrapper.ticketNumber = blankInfo.ticketNumber
                wrapper.operationDate = transInfo?.confirmDate
                wrapper.total = (status == ProductStatus.REFUND) ? MiscUtil.negate(blankInfo.amount) : blankInfo.amount
                wrapper.source = "UFS"

                ufsWrappers << wrapper
            }
        }
    }

    return ufsWrappers
}

def setAutoWidth() {
    nextRow()
    rowAutoHeight()

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

class UfsWrapper {
    ProductStatus status
    String reservationNumber
    String ticketNumber
    Date operationDate
    BigDecimal total
    String source
}