import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.dict.MCOCategory
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.parsers.model.R3ExcelExchangeDocumentIndex
import com.gridnine.xtrip.common.parsers.model.TransactionType
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportTicket
import com.gridnine.xtrip.common.search.SearchCriterion
import com.gridnine.xtrip.common.search.SearchQuery
import com.gridnine.xtrip.common.util.BooleanUtil
import com.gridnine.xtrip.common.util.MiscUtil
import com.gridnine.xtrip.common.util.TextUtil

//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')

//supplementary functions
def notNullNumber = {
    return it? it: BigDecimal.ZERO
}

def dateToLong = {
    if(!it){
        return 0
    }
    return MiscUtil.clearTime(it).getTime()
}

def equalsByTariff = { R3Wrapper t1, R3Wrapper t2 ->
    BigDecimal diff = notNullNumber(t1.equivalentFare)
    BigDecimal diffTax = notNullNumber(t1.taxesSum)
    BigDecimal diffCommissionValue = notNullNumber(t1.commissionValue)

    BigDecimal diff2 = notNullNumber(t2.equivalentFare)
    BigDecimal diffTax2 = notNullNumber(t2.taxesSum)
    BigDecimal diffCommissionValue2 = notNullNumber(t2.commissionValue)

    if (diff2.compareTo(BigDecimal.ZERO) != 0){
        if (diff2.compareTo(diff) == 0) {
            return true
        }
        if (diff2.compareTo(diffTax) == 0) {
            t1.taxesSum = BigDecimal.ZERO
            return true
        }
        if (diff2.compareTo(diffCommissionValue) == 0) {
            t1.commissionValue = BigDecimal.ZERO
            return true
        }
    } else if (diff.compareTo(BigDecimal.ZERO) != 0){
        if (diff.compareTo(diff2) == 0) {
            return true
        }
        if (diff.compareTo(diffTax2) == 0) {
            t2.taxesSum = BigDecimal.ZERO
            return true
        }
        if (diff.compareTo(diffCommissionValue2) == 0) {
            t2.commissionValue = BigDecimal.ZERO
            return true
        }
    } else if (diffTax2.compareTo(BigDecimal.ZERO) != 0){
        if (diffTax2.compareTo(diffCommissionValue) == 0) {
            t2.taxesSum = BigDecimal.ZERO
            t1.commissionValue = BigDecimal.ZERO
            return true
        }
    } else if (diffTax.compareTo(BigDecimal.ZERO) != 0) {
        if (diffTax.compareTo(diffCommissionValue2) == 0) {
            t1.taxesSum = BigDecimal.ZERO
            t2.commissionValue = BigDecimal.ZERO
            return true
        }
    }
    return diff.compareTo(diff2) == 0
}


def equalsByCommissionRate = {R3Wrapper t1, R3Wrapper t2 ->
    return notNullNumber(t1.commissionRate).compareTo(notNullNumber(t2.commissionRate)) == 0
}

def equalsByCommissionValue = {R3Wrapper t1, R3Wrapper t2 ->
    return notNullNumber(t1.commissionValue).compareTo(notNullNumber(t2.commissionValue)) == 0
}

def equalsByTaxesSum = {R3Wrapper t1, R3Wrapper t2 ->
    return notNullNumber(t1.taxesSum).compareTo(notNullNumber(t2.taxesSum)) == 0
}

def equalsByValidator = {R3Wrapper t1, R3Wrapper t2 ->
    if (t1.status == ProductStatus.VOID || t2.status == ProductStatus.VOID) {
        return true
    } else {
        return TextUtil.isSame(t1.validator, t2.validator)
    }
}

def equalsByDate = {R3Wrapper t1, R3Wrapper t2 ->
    return dateToLong(t1.operationDate) == dateToLong(t2.operationDate)
}

def equalsBuMainTicketNumber = {R3Wrapper t1, R3Wrapper t2 ->
    return equalsBuAnotherString(t1.mainTicketNumber, t2.mainTicketNumber)
}

def equalsByPassengerName = {R3Wrapper t1, R3Wrapper t2 ->
    return equalsBuAnotherString(t1.passengerName, t2.passengerName)
}

def equalsBuLuggageUnits = {R3Wrapper t1, R3Wrapper t2 ->
    return equalsBuAnotherString(t1.luggageUnits, t2.luggageUnits)
}

def equalsBuLuggageWeight = {R3Wrapper t1, R3Wrapper t2 ->
    return t1.luggageWeight == t2.luggageWeight
}

def equalsBuAnotherString(String t1, String t2){
    if (TextUtil.nonBlank(t1) ^ TextUtil.nonBlank(t2)) {
        return false
    }
    if (TextUtil.isBlank(t1) && TextUtil.isBlank(t2)) {
        return true
    }
    return t1.equals(t2)
}

def transactionTypeToStatus = {TransactionType type ->
    if(type == TransactionType.REFUND){
        return ProductStatus.REFUND
    }
    if(type == TransactionType.CANCEL){
        return ProductStatus.VOID
    }
    return ProductStatus.SELL
}

// Air ticket
def createWrapperFromProduct = {AirTicketsTemplateReportTicket prod ->
    R3Wrapper result = new R3Wrapper()
    result.source = "AGENCY"
    result.ticketNumber = prod.ticketNumber
    result.mainTicketNumber = prod.mainProductNumber
    result.conjCount = prod.conjCount ?: 0
    result.operationDate = prod.issueDate
    result.status = prod.status
    result.equivalentFare = prod.equivalentFare
    result.commissionRate = prod.vendorCommissionRate
    result.commissionValue = prod.vendorCommissionValue
    result.taxesSum = prod.taxesSum
    result.validator = prod.validator
    result.passengerName = prod.travellerName
    result.isMCO = prod.mcoCategory ? true : false
    result.mcoCategory = prod.mcoCategory
//    if (result.isMCO && result.mcoCategory == MCOCategory.EXCESS_LUGGAGE) {
    result.luggageUnits = prod.luggageUnits.name()
    result.luggageWeight = prod.luggageWeight
//    }
    return result
}

// Exchange document
def createWrapperFromDoc = {R3ExcelExchangeDocumentIndex prod ->
    R3Wrapper result = new R3Wrapper()
    result.source = "Yakutia"
    result.conjCount = prod.conjCount ?: 0
    if (prod.conjCount > 0) {
        int count = prod.conjCount
        long nextTicketLong = Long.parseLong(prod.ticketNumber)
        result.conjTicketNumber = prod.ticketNumber
        while (count > 0) {
            String nextTicketNumber = ++nextTicketLong.toString()
            result.conjTicketNumber = result.conjTicketNumber + '/' + nextTicketNumber.substring(nextTicketNumber.length()-1,nextTicketNumber.length())
            count--
        }
    }
    result.ticketNumber = prod.ticketNumber
    result.mainTicketNumber = prod.mainProductNumber
    result.operationDate = prod.operationDate
    result.status = transactionTypeToStatus(prod.transactionType)
    result.equivalentFare = prod.equivalentFare
    result.commissionRate = prod.commissionRate
    result.commissionValue = prod.commissionValue
    result.taxesSum = prod.taxesSum
    result.validator = prod.validator
    if (prod.traveller) {
        def entity = EntityStorage.get().resolve(prod.traveller)?.entity
        result.passengerName = entity?.name
    }
    result.isMCO = prod.isMCO
    result.mcoCategory = prod.mcoCategory
//    if (result.isMCO && result.mcoCategory == MCOCategory.EXCESS_LUGGAGE) {
        result.luggageUnits = prod.luggageUnits.toString()
        result.luggageWeight = prod.luggageWeight
//    }
    return result
}

//creating wrappers from products
def productWrappers = []
allTickets.each {AirTicketsTemplateReportTicket ticket ->
    productWrappers << createWrapperFromProduct(ticket)
}

//creating wrappers from R3 documents
SearchQuery query = new SearchQuery()
query.getCriteria().getCriterions().add(SearchCriterion.ge(R3ExcelExchangeDocumentIndex.Property.operationDate.name(), parameters['params'].periodBegin))
query.getCriteria().getCriterions().add(SearchCriterion.le(R3ExcelExchangeDocumentIndex.Property.operationDate.name(), parameters['params'].periodEnd))
def documentWrappers = []
EntityStorage.get().search(R3ExcelExchangeDocumentIndex.class, query).getData().each {R3ExcelExchangeDocumentIndex idx ->
    documentWrappers << createWrapperFromDoc(idx)
}

def addConjData = {R3Wrapper airProd, R3Wrapper conjFound ->
    airProd.commissionValue = notNullNumber(airProd.commissionValue).add(notNullNumber(conjFound.commissionValue))
    airProd.equivalentFare = notNullNumber(airProd.equivalentFare).add(notNullNumber(conjFound.equivalentFare))
    airProd.taxesSum = notNullNumber(airProd.taxesSum).add(notNullNumber(conjFound.taxesSum))
}

def conjTickets = { R3Wrapper airDoc, R3Wrapper airProd ->
    int count = airDoc.conjCount
    long nextTicket = Long.parseLong(airProd.ticketNumber)
    while (count > 0){
        nextTicket += 1
        R3Wrapper conjfound = productWrappers.find { R3Wrapper doc ->
            if (TextUtil.isSame(doc.ticketNumber, (nextTicket.toString()))){
                return true
            }
            return false
        }
        if(conjfound != null){
            addConjData(airProd, conjfound)
            String nextTicketStr = nextTicket.toString()
            airProd.ticketNumber = airProd.ticketNumber + '/' + nextTicketStr.substring(nextTicketStr.length()-1, nextTicketStr.length())
        }
        count--
    }

    return airProd
}

def resultWrappers = []
documentWrappers.each {R3Wrapper doc ->
    R3Wrapper conjTicket = null
    R3Wrapper found = productWrappers.find { R3Wrapper prod ->
        if (doc.status == prod.status) {
            if (TextUtil.isSame(doc.ticketNumber, prod.ticketNumber) && doc.conjCount == 0
            || TextUtil.isSame(doc.conjTicketNumber, prod.ticketNumber)) {
                return true
            } else if (doc.conjCount > 0 && doc.conjTicketNumber != null && doc.conjTicketNumber.contains("/") && !doc.conjTicketNumber.startsWith("/")) {
                if (TextUtil.isSame(prod.ticketNumber, doc.ticketNumber)) {
                    conjTicket = conjTickets(doc, prod)
                    return true
                }
            }
        }
        return false
    }

    if (found == null) {
        resultWrappers << doc
        return
    }

    productWrappers.remove(found)
    if (conjTicket){
        found = conjTicket
    }

    R3Wrapper item1 = new R3Wrapper()
    item1.source = doc.source
    item1.status = doc.status
    if (doc.conjCount > 0) {
        item1.ticketNumber = doc.conjTicketNumber
    } else {
        item1.ticketNumber = doc.ticketNumber
    }
    R3Wrapper item2 = new R3Wrapper()
    item2.source = found.source
    item2.status = found.status
    item2.ticketNumber = found.ticketNumber
    boolean hasDiff = false
    if (!equalsByCommissionRate(doc, found)) {
        hasDiff = true
        item1.commissionRate = doc.commissionRate
        item2.commissionRate = found.commissionRate
    }
    if (!equalsByCommissionValue(doc, found)) {
        hasDiff = true
        item1.commissionValue = doc.commissionValue
        item2.commissionValue = found.commissionValue
    }
    if (!equalsBuMainTicketNumber(doc, found)) {
        hasDiff = true
        item1.mainTicketNumber = doc.mainTicketNumber
        item2.mainTicketNumber = found.mainTicketNumber
    }
    if (!equalsBuLuggageUnits(doc, found)) {
        hasDiff = true
        item1.luggageUnits = doc.luggageUnits
        item2.luggageUnits = found.luggageUnits
    }
    if (!equalsBuLuggageWeight(doc, found)) {
        hasDiff = true
        item1.luggageWeight = doc.luggageWeight
        item2.luggageWeight = found.luggageWeight
    }
    if (!equalsByDate(doc, found)) {
        hasDiff = true
        item1.operationDate = doc.operationDate
        item2.operationDate = found.operationDate
    }
    if (!equalsByTariff(doc, found)) {
        hasDiff = true
        item1.equivalentFare = doc.equivalentFare
        item2.equivalentFare = found.equivalentFare
    }
        if (!equalsByPassengerName(doc, found)) {
        hasDiff = true
        item1.passengerName = doc.passengerName
        item2.passengerName = found.passengerName
    }
    if (!equalsByTaxesSum(doc, found)) {
        hasDiff = true
        item1.taxesSum = doc.taxesSum
        item2.taxesSum = found.taxesSum
    }
    if (!BooleanUtil.nullAsFalse(parameters["not_check_validators"])) {
        if (!equalsByValidator(doc, found)) {
            hasDiff = true
            item1.validator = doc.validator
            item2.validator = found.validator
        }
    }
    if (hasDiff) {
        resultWrappers << item1
        resultWrappers << item2
    }

}
resultWrappers.addAll(productWrappers)

resultWrappers.sort{R3Wrapper it -> it.ticketNumber + it.source + it.status}
allTickets.clear()
allTickets.addAll(resultWrappers)


//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()
    columnWidth(12)
    text('Номер билета')
    nextColumn()
    columnWidth(10)
    text('Дата')
    nextColumn()
    columnWidth(10)
    text('Статус')
    nextColumn()
    columnWidth(12)
    text('Тариф')
    nextColumn()
    columnWidth(10)
    text('% комиссии')
    nextColumn()
    columnWidth(10)
    text('Сумма комиссии')
    nextColumn()
    columnWidth(10)
    text('Таксы')
    nextColumn()
    columnWidth(10)
    text('Валидатор')
    nextColumn()
    columnWidth(12)
    text('Номер основного билета')
    nextColumn()
    columnWidth(36)
    text('Пассажир')
    nextColumn()
    columnWidth(10)
    text('Единица измерения (багаж)')
    nextColumn()
    columnWidth(10)
    text('Количество (багаж)')
    nextRow()
    tickets{R3Wrapper wrapper ->
        rowHeight(12)
        text(wrapper.source,'textData')
        nextColumn()
        text(wrapper.ticketNumber,'textData')
        nextColumn()
        date(wrapper.operationDate,'dateData')
        nextColumn()
        text(wrapper.status?.toString(),'textData')
        nextColumn()
        number(wrapper.equivalentFare,'numberData')
        nextColumn()
        number(wrapper.commissionRate,'numberData')
        nextColumn()
        number(wrapper.commissionValue,'numberData')
        nextColumn()
        number(wrapper.taxesSum,'numberData')
        nextColumn()
        text(wrapper.validator,'textData')
        nextColumn()
        text(wrapper.mainTicketNumber,'textData')
        nextColumn()
        text(wrapper.passengerName,'textData')
        nextColumn()
        text(wrapper.luggageUnits,'textData')
        nextColumn()
        number(wrapper.luggageWeight,'numberData')
        nextRow()
    }

}

class R3Wrapper{

    String ticketNumber

    String mainTicketNumber

    Date operationDate

    ProductStatus status

    BigDecimal equivalentFare

    BigDecimal commissionRate

    BigDecimal commissionValue

    BigDecimal taxesSum

    String validator

    String source

    int conjCount = 0

    String conjTicketNumber

    String passengerName

    boolean isMCO

    MCOCategory mcoCategory

    String luggageUnits

    Double luggageWeight
}