import com.gridnine.xtrip.common.Environment
import com.gridnine.xtrip.common.l10n.model.LocaleHelper
import com.gridnine.xtrip.common.model.ClientFop
import com.gridnine.xtrip.common.model.EntityContainer
import com.gridnine.xtrip.common.model.EntityReference
import com.gridnine.xtrip.common.model.booking.OperationBatch
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.TransportationType
import com.gridnine.xtrip.common.model.booking.air.Tax
import com.gridnine.xtrip.common.model.dict.CommunicationType
import com.gridnine.xtrip.common.model.dict.DictionaryCache
import com.gridnine.xtrip.common.model.dict.MCOCategory
import com.gridnine.xtrip.common.model.dict.ProductCategory
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.helpers.FinanceHelper
import com.gridnine.xtrip.common.model.profile.Address
import com.gridnine.xtrip.common.model.profile.Communication
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.model.system.Metadata
import com.gridnine.xtrip.common.model.system.MetadataKey
import com.gridnine.xtrip.common.model.system.MetadataKeyDescriptor
import com.gridnine.xtrip.common.model.system.PaymentType
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportParameters
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportTicket
import com.gridnine.xtrip.common.util.MiscUtil
import com.gridnine.xtrip.common.util.TextUtil

//CREATING STYLES
createStyle(name: 'baseStyle', fontBold: true,  h_alignment: 'LEFT', v_alignment: 'CENTER', fontHeight:12, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN')
createStyle(name: 'style', fontBold: false, fontHeight: 11, parent: 'baseStyle')
createStyle(name: 'textData', parent: 'style',  h_alignment: 'RIGHT')
createStyle(name: 'otherTextData', parent: 'style',  h_alignment: 'RIGHT')
createStyle(name: 'textDataBold', parent: 'baseStyle')
createStyle(name: 'dateData', format: 'm/d/yy', parent: 'style')
createStyle(name: 'dateDataBold', format: 'm/d/yy', parent: 'baseStyle')
createStyle(name: 'numberData', format: '#,##0.00', parent: 'style',  h_alignment: 'RIGHT')
createStyle(name: 'emptyNumberData', parent: 'style')
createStyle(name: 'numberDataBold', format: '#,##0.00', parent: 'baseStyle',  h_alignment: 'RIGHT')
createStyle(name: 'stampData', h_alignment: 'LEFT', v_alignment: 'CENTER', fontHeight:12)
createStyle(name: 'stampText', h_alignment: 'CENTER', v_alignment: 'BOTTOM', fontHeight:7)
createStyle(name: 'integerNumberText', format: '#0', parent: 'style', h_alignment: 'RIGHT')

//ADDITIONAL FUNCTIONS
class Formula {
    String formula
    Formula(){}
    Formula(String formula) {
        this.formula = formula
    }

    static Formula getSumFormulaByRow (String start, String end) {
        return new Formula('SUM(' + start + ':' + end + ')')
    }
    static Formula getSumFormulaByCell(String... index) {
        def formula = ''
        for (int i=0; i<index.size(); i++) {
            if(i == 0) {
                formula += index[i]
            } else {
                formula += ',' + index[i]
            }
        }
        return new Formula('SUM(' + formula + ')')
    }
}

String[] transportationTypes    = ['vvl', 'mvl']

def cellFill = { value ->
    if (value != null) {
        if (value instanceof Number) {
            number(value,'numberData')
        } else if (value instanceof Date) {
            date(value,'dateData')
        } else if (value instanceof Formula){
            formula(value.formula,'numberData')
        } else {
            text(value, 'otherTextData')
        }
    }
}

def intFill = { value ->
    if (value != null) {
        if (value instanceof Number) {
            number(value,'integerNumberText')
        } else if (value instanceof Formula) {
            formula(value.formula, 'integerNumberText')
        } else {
            text(value, 'integerNumberText')
        }
    }
}

def cellFillBold = { value ->
    if (value != null) {
        if (value instanceof Number) {
            number(value,'numberDataBold')
        } else if (value instanceof Date) {
            date(value,'dateDataBold')
        } else {
            text(value, 'textDataBold')
        }
    }
}

def getOrganization = { EntityReference<Organization> erO ->
    EntityContainer<Organization> ecO = EntityStorage.get().resolve(erO)
    return ecO?.entity
}

def getOrganizationCode = { Organization org ->
    def utAgencyCode = ''
    for(Metadata md : org.metadata) {
        MetadataKeyDescriptor mdKd = Environment.getPublished(DictionaryCache.class).resolveReference(md.key)
        if (mdKd != null) {
            if(MetadataKey.KEY_UT_AGENCY_CODE.name().equals(mdKd.code)) {
                utAgencyCode  = md.value.toString()
            }
        }
    }
    return utAgencyCode
}

def getOrganizationCountry = {Organization org ->
    def defaultCountry = ''
    org.addresses.each {Address address ->
        String country = address.country.toString(LocaleHelper.RU_LOCALE)
        if(TextUtil.nonBlank(country)) {
            defaultCountry = country
        }
    }
    return defaultCountry
}

def getOrganizationPhone = {Organization org ->
    def phone = ''
    org?.communications?.each {Communication communication->
        if(communication.type == CommunicationType.WORK_PHONE) {
            phone = communication.countryCode + communication.cityCode + communication.sense
        }
    }
    return phone
}

def reportHeader = { EntityReference<Organization> orgRef ->

    def scw = 12

    Organization org = getOrganization(orgRef)
    def orgName = ''
    def orgCode = ''
    def orgCountry = ''
    def orgPhone = ''
    def currency = FinanceHelper.getDefaultCurrency().code
    def currentDate = new Date()

    if (org != null) {
        orgName = org.fullName.toString(LocaleHelper.RU_LOCALE)
        orgCode = getOrganizationCode(org)
        orgCountry = getOrganizationCountry(org)
        orgPhone = getOrganizationPhone(org)
    }

    rowHeight(2*scw)
    columnWidth(scw)
    text('Авиакомпания', 'baseStyle', 2, 1)
    nextColumn()
    columnWidth(30)
    nextColumn()
    columnWidth(2.5*scw)
    text('ЮТЭЙР', 'baseStyle')
    nextColumn()
    columnWidth(1.5*scw)
    text('Код:', 'baseStyle')
    nextColumn()
    columnWidth(2*scw)
    text('UT', 'baseStyle')
    nextColumn()
    columnWidth(2*scw)
    text('Отчет', 'baseStyle')
    nextColumn()
    columnWidth(2*scw)
    text('Расчетное письмо', 'baseStyle')
    nextRow()
    rowHeight(2*scw)
    text('Агент', 'baseStyle', 2, 1)
    nextColumn()
    columnWidth(30)
    nextColumn()
    text(orgName, 'baseStyle')
    nextColumn()
    text('Код:', 'baseStyle')
    nextColumn()
    text(orgCode, 'baseStyle')
    nextColumn()
    text('Период', 'baseStyle')
    nextColumn()
    text(parameters.REPORT_PERIOD, 'dateDataBold')
    nextRow()
    rowHeight(2*scw)
    text('Страна', 'baseStyle', 2, 1)
    nextColumn()
    columnWidth(30)
    nextColumn()
    text(orgCountry, 'baseStyle')
    nextColumn()
    text('Валюта', 'baseStyle')
    nextColumn()
    text(currency, 'baseStyle')
    nextColumn()
    text('Дата', 'baseStyle')
    nextColumn()
    date(currentDate, 'dateDataBold')
    nextRow()
    rowHeight(2*scw)
    text('Контактный телефон', 'baseStyle', 2, 1)
    nextColumn()
    columnWidth(30)
    nextColumn()
    text(orgPhone, 'baseStyle', 1 , 1)
    1.times {nextColumn()}
    text(parameters['contract_number'], 'baseStyle', 4, 1)
}

def reportEnd = {
    nextRow()
    rowHeight(27)
    text('Генеральный директор', 'stampData', 3, 2)
    3.times {nextColumn()}
    text('________________', 'stampText')
    nextColumn()
    text(parameters['ceo'], 'stampData', 2, 2)
    nextRow()
    rowHeight(10)
    3.times {nextColumn()}
    text('(Подпись)','stampText')
    nextRow()
    rowHeight(27)
    text('Главный бухгалтер', 'stampData', 3, 2)
    3.times {nextColumn()}
    text('________________', 'stampText')
    nextColumn()
    text(parameters['main_accountant'], 'stampData', 2, 2)
    nextRow()
    rowHeight(10)
    3.times {nextColumn()}
    text('(Подпись)','stampText')
    nextRow()
}

def tableHeader = {
    2.times {nextRow()}
    rowHeight(20)
    text('№', 'baseStyle')
    nextColumn()
    text('Раздел', 'baseStyle', 3, 1)
    3.times {nextColumn()}
    text('ВВЛ', 'baseStyle')
    nextColumn()
    text('МВЛ', 'baseStyle')
    nextColumn()
    text('Всего', 'baseStyle')
    nextColumn()
}


def getTransportationTypeList = {Map<String,Object> mainMap, String transportationType ->
    List<AirTicketsTemplateReportTicket> list = mainMap?.get(transportationType)
    if(list == null){
        list = new ArrayList<AirTicketsTemplateReportTicket>()
        mainMap.put(transportationType,list)
    }
    return list
}

def sortByTransportationType = { AirTicketsTemplateReportTicket ticket, Map<String,Object> mainMap ->
    if(ticket.transportationType == TransportationType.DOMESTIC) {
        getTransportationTypeList(mainMap,transportationTypes[0]).add(ticket)
    } else  if(ticket.transportationType == TransportationType.INTERNATIONAL
            || ticket.transportationType == TransportationType.COMBINED) {
        getTransportationTypeList(mainMap,transportationTypes[1]).add(ticket)
    }

}

def createTicketMap = {
    Map<String,Object> mainMap = new HashMap<String,Object>()

    allTickets.each { AirTicketsTemplateReportTicket ticket ->
        sortByTransportationType(ticket, mainMap)
    }
    return mainMap
}

def getMTDTariff = { AirTicketsTemplateReportTicket ticket ->
    for (ClientFop fop : ticket.clientFops) {
        if (PaymentType.MTD == fop.type
            || PaymentType.CREDIT == fop.type) {
            return ticket.equivalentFare ?: BigDecimal.ZERO
        }
    }
    return BigDecimal.ZERO
}

def getMTDTaxes = { AirTicketsTemplateReportTicket ticket ->
    for (ClientFop fop : ticket.clientFops) {
        if (PaymentType.MTD == fop.type
            || PaymentType.CREDIT == fop.type) {
            return ticket.taxesSum ?: BigDecimal.ZERO
        }
    }
    return BigDecimal.ZERO
}

def getTaxesSa = { List<Tax> taxes ->
    BigDecimal result = BigDecimal.ZERO
    taxes.each {Tax tax ->
        if(TextUtil.isSame(tax.code, 'SA') && tax.equivalentAmount != null) {
            result = MiscUtil.sum(result, tax.equivalentAmount)
        }
    }
    return result
}

def makeCalculation = { List<AirTicketsTemplateReportTicket> list ->
    def result = []

    def sellTariff = BigDecimal.ZERO
    def changePayments = BigDecimal.ZERO
    def sellLuggagePayments = BigDecimal.ZERO
    def additionalServicesPayments = BigDecimal.ZERO
    def sellTaxes = BigDecimal.ZERO
    def sellSaTaxes = BigDecimal.ZERO
    def sellAgentReward = BigDecimal.ZERO
    def sellMTDTariff = BigDecimal.ZERO
    def sellMTDTaxes = BigDecimal.ZERO

    def refundTariff = BigDecimal.ZERO
    def refundTaxes = BigDecimal.ZERO
    def refundLuggagePayments = BigDecimal.ZERO
    def refundReward = BigDecimal.ZERO
    def refundMTDTariff = BigDecimal.ZERO
    def refundMTDTaxes = BigDecimal.ZERO

    def sellTicketsCount = 0
    def refundTicketsCount = 0

    list.each { AirTicketsTemplateReportTicket ticket ->
        def tariff = BigDecimal.ZERO
        def luggage = BigDecimal.ZERO
        if((ticket.productCategory == ProductCategory.MCO && ticket.mcoCategory == MCOCategory.EXCESS_LUGGAGE)
                || ticket.productCategory == ProductCategory.EXCESS_BAGAGE) {
            luggage = MiscUtil.sum(luggage, ticket.equivalentFare)
        }  else {
            tariff = MiscUtil.sum(tariff, ticket.equivalentFare)
        }

        if(ticket.operationBatch == OperationBatch.SELL) {
            if (ticket.productCategory == ProductCategory.MCO
                    && (ticket.mcoCategory == MCOCategory.REBOOKING
                    || ticket.mcoCategory == MCOCategory.PENALTY)) {
                if (ticket.mcoCategory == MCOCategory.REBOOKING){
                    changePayments = MiscUtil.sum(changePayments, ticket.equivalentFare)
                } else if (ticket.mcoCategory == MCOCategory.PENALTY) {
                    changePayments = MiscUtil.sum(changePayments, ticket.penalty)
                }
            } else if(ticket.productCategory == ProductCategory.MCO
                    && ticket.mcoCategory != MCOCategory.REBOOKING
                    && ticket.mcoCategory != MCOCategory.EXCESS_LUGGAGE) {
                additionalServicesPayments = MiscUtil.sum(additionalServicesPayments, ticket.equivalentFare)
            } else {
                sellTariff = MiscUtil.sum(sellTariff, tariff)
                sellTaxes = MiscUtil.sum(sellTaxes, ticket.taxesSum)
                sellLuggagePayments = MiscUtil.sum(sellLuggagePayments, luggage)
                sellAgentReward = MiscUtil.sum(sellAgentReward, ticket.vendorCommissionValue)
                sellTicketsCount++
            }
            sellMTDTariff = MiscUtil.sum(sellMTDTariff, getMTDTariff(ticket))
            sellMTDTaxes = MiscUtil.sum(sellMTDTaxes, getMTDTaxes(ticket))
            sellSaTaxes = MiscUtil.sum(sellSaTaxes, getTaxesSa(ticket.taxes))
        } else if(ticket.operationBatch == OperationBatch.EXCHANGE) {
            sellTariff = MiscUtil.sum(sellTariff, tariff)
            sellTaxes = MiscUtil.sum(sellTaxes, ticket.taxesSum)
            sellMTDTariff = MiscUtil.sum(sellMTDTariff, getMTDTariff(ticket))
            sellMTDTaxes = MiscUtil.sum(sellMTDTaxes, getMTDTaxes(ticket))
            sellSaTaxes = MiscUtil.sum(sellSaTaxes, getTaxesSa(ticket.taxes))
            sellTicketsCount++
        } else if(ticket.operationBatch == OperationBatch.REFUND) {
            if (ticket.productCategory == ProductCategory.MCO
                    && (ticket.mcoCategory == MCOCategory.REBOOKING
                    || ticket.mcoCategory == MCOCategory.PENALTY)) {
                if (ticket.mcoCategory == MCOCategory.REBOOKING){
                    changePayments = MiscUtil.sum(changePayments, ticket.equivalentFare)
                } else if (ticket.mcoCategory == MCOCategory.PENALTY) {
                    changePayments = MiscUtil.sum(changePayments, ticket.penalty)
                }
                sellAgentReward = MiscUtil.sum(sellAgentReward, ticket.vendorCommissionValue)
            } else {
                refundReward = MiscUtil.sum(refundReward, ticket.vendorCommissionValue)
            }
            refundTariff = MiscUtil.sum(refundTariff, tariff)
            refundTaxes = MiscUtil.sum(refundTaxes, ticket.taxesSum)
            refundLuggagePayments = MiscUtil.sum(refundLuggagePayments, luggage)
            refundMTDTariff = MiscUtil.sum(refundMTDTariff, getMTDTariff(ticket))
            refundMTDTaxes = MiscUtil.sum(refundMTDTaxes, getMTDTaxes(ticket))
            if (ticket.status == ProductStatus.REFUND) {
                if (ticket.mcoCategory == null || ticket.mcoCategory == MCOCategory.EXCESS_LUGGAGE) {
                    refundTicketsCount++
                }
            }
        }
    }

    result.add(MiscUtil.sub(sellTariff, sellMTDTariff))                 //0
    result.add(changePayments)                                          //1
    result.add(sellLuggagePayments)                                     //2
    result.add(additionalServicesPayments)                              //3
    result.add(MiscUtil.sub(sellTaxes, sellMTDTaxes, sellSaTaxes))      //4
    result.add(sellAgentReward)                                         //5
    result.add(sellMTDTariff)                                           //6
    result.add(sellMTDTaxes)                                            //7
    result.add(MiscUtil.sub(refundTariff, refundMTDTariff))             //8
    result.add(MiscUtil.sub(refundTaxes, refundMTDTaxes))               //9
    result.add(refundLuggagePayments)                                   //10
    result.add(refundReward)                                            //11
    result.add(refundMTDTariff)                                         //12
    result.add(refundMTDTaxes)                                          //13
    result.add(sellTicketsCount)                                        //14
    result.add(refundTicketsCount)                                      //15
    result.add(sellSaTaxes)                                             //16

    return result
}

def fillTableResult = {Map<String,Object> map ->
    def table = []

    def tableVvl = makeCalculation(map.get(transportationTypes[0]))

    def tableMvl = makeCalculation(map.get(transportationTypes[1]))

    def rowShift    = 1
    def columnShift = -3

    def createFormulaForRow = {int row ->
        return Formula.getSumFormulaByRow(cellIndex(rowShift + row,columnShift), cellIndex(rowShift + row,columnShift+1))
    }

    def createFormulaForSell = {int column ->
        return Formula.getSumFormulaByCell(cellIndex(3, columnShift + column),
                cellIndex(4, columnShift + column), cellIndex(5, columnShift + column),
                cellIndex(6, columnShift + column), cellIndex(7, columnShift + column),
                cellIndex(8, columnShift + column), '-' + cellIndex(9, columnShift + column))
    }

    def createFormulaForRefund = {int column ->
        return Formula.getSumFormulaByCell(cellIndex(13, columnShift + column),
                cellIndex(14, columnShift + column),
                cellIndex(15, columnShift + column),
                cellIndex(16, columnShift + column),
                '-' + cellIndex(17, columnShift + column))
    }

    def createFormulaForBalance = {int column ->
        return Formula.getSumFormulaByCell(cellIndex(1, columnShift  + column),
                cellIndex(24, columnShift  + column), '-' + cellIndex(27, columnShift  + column))
    }

    def createFormulaForTransfer = {int column ->
        return Formula.getSumFormulaByCell(cellIndex(22, columnShift + column),
                '+' + cellIndex(23, columnShift  + column))
    }

    def createFormulaForAgencyReward = {int column ->
        return Formula.getSumFormulaByCell(cellIndex(9, columnShift + column),
                '-' + cellIndex(17, columnShift + column))
    }

    def createFormulaForTotal = {int column ->
        return Formula.getSumFormulaByCell(cellIndex(2, columnShift + column),
                '-' + cellIndex(12, columnShift + column))
    }

    table.add([''   ,  'Сальдо на начало периода'                 , '' , '', createFormulaForRow(0)])
    table.add(['1'  ,  'Продажа'                                  , createFormulaForSell(0), createFormulaForSell(1), createFormulaForRow(1)])
    table.add(['1.1',  'Пассажирский тариф'                       , tableVvl[0], tableMvl[0], createFormulaForRow(2)])
    table.add(['1.2',  'Сбор/плата за изменение условий'          , tableVvl[1], tableMvl[1], createFormulaForRow(3)])
    table.add(['1.3',  'Плата за сверхнормативный багаж'          , tableVvl[2], tableMvl[2], createFormulaForRow(4)])
    table.add(['1.4',  'Сбор/плата за дополнительные сервисы'     , tableVvl[3], tableMvl[3], createFormulaForRow(5)])
    table.add(['1.5',  'Таксы'                                    , tableVvl[4], tableMvl[4], createFormulaForRow(6)])
    table.add(['1.6',  'Сбор SA                   '               , tableVvl[16], tableMvl[16], createFormulaForRow(7)])
    table.add(['1.7',  'Агентское вознаграждение'                 , tableVvl[5], tableMvl[5], createFormulaForRow(8)])
    table.add(['1.8',  'ВПД МВД (тариф)'                          , tableVvl[6], tableMvl[6], createFormulaForRow(9)])
    table.add(['1.9',  'ВПД МВД (сборы)'                          , tableVvl[7], tableMvl[7], createFormulaForRow(10)])
    table.add(['2'  ,  'Возврат'                                  , createFormulaForRefund(0), createFormulaForRefund(1), createFormulaForRow(11)])
    table.add(['2.1',  'Пассажирский тариф'                       , tableVvl[8], tableMvl[8], createFormulaForRow(12)])
    table.add(['2.2',  'Таксы'                                    , tableVvl[9], tableMvl[9], createFormulaForRow(13)])
    table.add(['2.3',  'Сбор за использование МАСБ'               , '', '', createFormulaForRow(14)])
    table.add(['2.4',  'Плата за сверхнормативный багаж'          , tableVvl[10], tableMvl[10], createFormulaForRow(15)])
    table.add(['2.5',  'Агентское вознаграждение'                 , tableVvl[11], tableMvl[11], createFormulaForRow(16)])
    table.add(['2.6',  'ВПД МВД (тариф)'                          , tableVvl[12], tableMvl[12], createFormulaForRow(17)])
    table.add(['2.7',  'ВПД МВД (сборы)'                          , tableVvl[13], tableMvl[13], createFormulaForRow(18)])
    table.add(['3'  ,  'Корректировка коммиссии'                  , '', '', createFormulaForRow(19)])
    table.add(['4'  ,  'Итого агентское вознаграждение'           , createFormulaForAgencyReward(0), createFormulaForAgencyReward(1), createFormulaForRow(20)])
    table.add(['5'  ,  'Итого'                                    , createFormulaForTotal(0), createFormulaForTotal(1), createFormulaForRow(21)])
    table.add(['6'  ,  'Корректировка тарифа'                     , '', '', createFormulaForRow(22)])
    table.add(['7'  ,  'Итого подлежит перечислению'              , createFormulaForTransfer(0), createFormulaForTransfer(1), createFormulaForRow(23)])
    table.add(['8'  ,  'Продано документов'                       , tableVvl[14], tableMvl[14], createFormulaForRow(24)])
    table.add(['9'  ,  'Возвращено документов'                    , tableVvl[15], tableMvl[15], createFormulaForRow(25)])
    table.add(['10' ,  'Перечислено'                              , '', '', createFormulaForRow(26)])
    table.add([''   ,  'Сальдо на конец периода'                  , createFormulaForBalance(0), createFormulaForBalance(1), createFormulaForRow(27)])

    return table
}

def tableBody = {
    Map<String,Object> map = createTicketMap()
    def table = fillTableResult(map)

    table.each { row ->
        if(row[0] =~ /^8/ || row[0] =~ /^9/ || row[0] =~ /^10/){
            nextRow()
            rowHeight(15)
            text(row[0], 'style')
            nextColumn()
            text(row[1], 'style', 3, 1)
            3.times { nextColumn() }
            intFill(row[2])
            nextColumn()
            intFill(row[3])
            nextColumn()
            intFill(row[4])
            nextColumn()
        } else {
            nextRow()
            rowHeight(15)
            text(row[0], 'style')
            nextColumn()
            text(row[1], 'style', 3, 1)
            3.times { nextColumn() }
            cellFill(row[2])
            nextColumn()
            cellFill(row[3])
            nextColumn()
            cellFill(row[4])
            nextColumn()
        }
    }
}

def reportBody = {
    tableHeader()
    tableBody()
}

page{'Расчетное письмо'} {

    fitWidth(1)
    fitHeight(1)
    landscape(false)
    scale(80)

    warn 'report version = ' + '0.1.0'

    AirTicketsTemplateReportParameters airTicketParams = parameters.params
    EntityReference<Organization> orgRef = airTicketParams.agency

    reportHeader(orgRef)
    reportBody()
    reportEnd(orgRef)
}