import com.gridnine.xtrip.common.Environment
import com.gridnine.xtrip.common.l10n.model.LocaleHelper
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.SimpleTax
import com.gridnine.xtrip.common.model.booking.air.Tax
import com.gridnine.xtrip.common.model.dict.*
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.helpers.ProfileHelper
import com.gridnine.xtrip.common.model.profile.Communication
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.model.profile.Person
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.AirTicketsTemplateReportSegmentTariff
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportTicket
import com.gridnine.xtrip.common.util.MiscUtil

//CREATING STYLES
createStyle(name: 'title', h_alignment: 'CENTER', v_alignment: 'CENTER')
createStyle(name: 'titleH1',fontBold: true, fontHeight: 12, parent: 'title')
createStyle(name: 'titleH2',fontBold: false,h_alignment: 'LEFT', fontHeight: 9, parent: 'title')
createStyle(name: 'header', fontBold: true,  h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:9, leftBorder:'MEDIUM', rightBorder:'MEDIUM', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
createStyle(name: 'columnHeader', fontHeight: 9, parent: 'header')
createStyle(name: 'rowHeader',fontHeight: 9, h_alignment: 'LEFT', wrapText: true, parent: 'titleH2')
createStyle(name: 'data', h_alignment: 'LEFT', v_alignment: 'CENTER', fontHeight: 7)
createStyle(name: 'textData',fontHeight: 9 ,parent: 'data')
createStyle(name: 'textDataBold', parent: 'data',fontBold: true)
createStyle(name: 'dateData', format: 'm/d/yy', parent: 'data')
createStyle(name: 'dateDataBold', format: 'm/d/yy', parent: 'data',fontBold: true)
createStyle(name: 'numberData', h_alignment: 'RIGHT', format: '#,##0.00', parent: 'data')
createStyle(name: 'emptyNumberData', h_alignment: 'RIGHT', parent: 'data')
createStyle(name: 'numberDataBold', h_alignment: 'RIGHT', format: '#,##0.00', parent: 'data',fontBold: true)
createStyle(name: 'metadataTitle',fontBold: true, h_span: 2, h_alignment: 'RIGHT', v_alignment: 'CENTER', fontHeight:10)
createStyle(name: 'metadataValue', parent: 'metadataTitle', h_span: 10, fontBold: false, h_alignment: 'LEFT')
createStyle(name: 'metadataDateValue', parent: 'metadataValue', format: 'm/d/yy')
createStyle(name: 'statusGroupTitle', parent: 'header', h_span: 4, fontBold: true)
createStyle(name: 'statusGroupNumber',parent: 'header', h_span: 2)
createStyle(name: 'subagencyGroupTitle', parent: 'statusGroupTitle')
createStyle(name: 'GroupNumber',parent: 'header', v_span: 3)
createStyle(name: 'totalTitle', parent: 'subagencyGroupTitle')
createStyle(name: 'totalNumber',parent: 'totalTitle', h_span: 1)

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

def notNullBigDec = {BigDecimal it ->
    return it == null ? BigDecimal.ZERO : it
}

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, 'textData')
        }
    }
}

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 irAgencyCode = ''
    for(Metadata md : org.metadata) {
        MetadataKeyDescriptor mdKd = Environment.getPublished(DictionaryCache.class).resolveReference(md.key)
        if (mdKd != null) {
            if(MetadataKey.KEY_IR_AGENCY_CODE.name().equals(mdKd.code)) {
                irAgencyCode = md.value.toString()
            }
        }
    }
    return irAgencyCode
}

def reportHeader = { EntityReference<Organization> orgRef ->
    Organization org = getOrganization(orgRef)
    def orgName     = ''
    def orgCode  = ''

    if (org != null) {
        orgName     = org.fullName.toString()
        orgCode  = getOrganizationCode(org)
    }

    rowHeight(30)
    nextColumn()
    text('Расчетное письмо', 'titleH1')
    nextRow()
    nextColumn()
    text(orgName + ' код - ' + orgCode, 'titleH1')
    nextRow()
    nextColumn()
    text(parameters.REPORT_PERIOD, 'titleH1')
    nextRow()
    nextColumn()
    rowHeight(30)
    text('по авиакомпании ЗАО АК ИрАэро', 'titleH1')
}

def reportEnd = { EntityReference<Organization> orgRef ->
    def mainAccountant  = ''
    def director        = ''
    def performer       = ''
    def phone           = ''
    def orgName         = ''

    Organization org = getOrganization(orgRef)
    if (org != null) {
        orgName = org.fullName.toString()
        org?.communications?.any { Communication comm ->
            if (comm.type == CommunicationType.WORK_PHONE || comm.type == CommunicationType.AGENCY) {
                phone = comm.sense
            }
        }
        EntityReference<Person> erP = ProfileHelper.getManager(orgRef, ManagerType.CHIEF_ACCOUNTANT, new Date())
        if(erP != null){
            EntityContainer<Person> ecP = EntityStorage.get().resolve(erP)
            mainAccountant = ProfileHelper.getFullName(ecP.entity,LocaleHelper.getCurrentLocale(), false)
        }
        erP = ProfileHelper.getManager(orgRef, ManagerType.DIRECTOR, new Date())
        if(erP != null){
            EntityContainer<Person> ecP = EntityStorage.get().resolve(erP)
            mainAccountant = ProfileHelper.getFullName(ecP.entity,LocaleHelper.getCurrentLocale(), false)
        }

        rowHeight(13)
        3.times{nextRow()}
        nextColumn();cellFillBold('Директор')
        nextColumn();cellFillBold(orgName)
        nextColumn();cellFillBold(director)

        nextRow()
        nextColumn()
        cellFillBold('Главный бухгалтер')
        2.times{nextColumn()};cellFillBold(mainAccountant)

        nextRow()
        nextColumn()
        cellFillBold('Исполнитель')
        2.times{nextColumn()};cellFillBold(performer)

        nextRow()
        nextColumn()
        cellFillBold('Телефон')
        nextColumn()
        cellFillBold(phone)
    }
}

def tableHeader = {

    int scw = 10

    rowHeight(13);

    2.times{nextRow()}
    setStyle('columnHeader')
    rowHeight(15)
    columnWidth(scw);
    text('№п/п', 'GroupNumber')
    nextColumn();columnWidth(4*scw);text('Наименование статьи выручки', 'GroupNumber')
    nextColumn();columnWidth(2*scw);text('Всего', 'GroupNumber')
    nextRow()
    setStyle('columnHeader')
    nextRow()
    setStyle('columnHeader')
    3.times{nextColumn()}
    nextRow()
    text('1')
    nextColumn();text('2')
    nextColumn();text('3')
    nextRow()
    rowHeight(12)
}


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 ->
    getTransportationTypeList(mainMap,transportationTypes[0]).add(ticket)
}

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

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



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

    def sell               = BigDecimal.ZERO
    def sellTariff         = BigDecimal.ZERO
    def sellTaxBlank       = BigDecimal.ZERO
    def sellTaxAgs         = BigDecimal.ZERO
    def sellVPD            = BigDecimal.ZERO
    def sellPenalties      = BigDecimal.ZERO

    def exchange           = BigDecimal.ZERO
    def exchangeAddTariff  = BigDecimal.ZERO
    def exchangeTaxBlank   = BigDecimal.ZERO
    def exchangeTaxAgs     = BigDecimal.ZERO
    def exchangePenalties  = BigDecimal.ZERO

    def luggage            = BigDecimal.ZERO

    def refund             = BigDecimal.ZERO
    def refundAddTariff    = BigDecimal.ZERO
    def refundTaxBlank     = BigDecimal.ZERO
    def refundTaxAgs       = BigDecimal.ZERO
    def penalty            = BigDecimal.ZERO
    def penaltyVPD         = BigDecimal.ZERO
    def refundVPD          = BigDecimal.ZERO
    def refundPenalties    = BigDecimal.ZERO

    def reward             = BigDecimal.ZERO
    def rewardSell         = BigDecimal.ZERO
    def rewardExchange     = BigDecimal.ZERO
    def rewardRefund       = BigDecimal.ZERO
    def rewardVDP          = BigDecimal.ZERO

    def totalToIrAero      = BigDecimal.ZERO

    def totalPenlties      = BigDecimal.ZERO

    def rewardRefundVPD    = BigDecimal.ZERO
    def rewardSellVPD      = BigDecimal.ZERO

    int amountTickets = 0
    int amountVoidTickets = 0
    int amountExchangeTickets = 0


    list.each { AirTicketsTemplateReportTicket ticket ->

        def taxAgs = BigDecimal.ZERO
        def taxBlank = BigDecimal.ZERO
        def includeZZTax = parameters['includeZZTax']

        ticket.taxes.each { Tax tax ->
            if (tax.code == ('YQ') && (tax.equivalentAmount != null)) {
                taxAgs = MiscUtil.sum(taxAgs, tax.equivalentAmount)
            } else if (tax.code == ('YR') && (tax.equivalentAmount != null)) {
                taxBlank = MiscUtil.sum(taxBlank, tax.equivalentAmount)
            } else if (tax.code == ('ZZ') && (tax.equivalentAmount != null)) {
                if (includeZZTax) {
                    taxBlank = MiscUtil.sum(taxBlank, tax.equivalentAmount) //Show ZZ tax as YR (XTR-9643)
                }
            }
        }
        if (ticket.taxes.size() == 0) {
            ticket.segmentTariffs.each { AirTicketsTemplateReportSegmentTariff tariff ->
                tariff.taxes.each { SimpleTax tax ->
                    if (tax.code == ('YQ') && (tax.amount != null)) {
                        taxAgs = MiscUtil.sum(taxAgs, tax.amount)
                    } else if (tax.code == ('YR') && (tax.amount != null)) {
                        taxBlank = MiscUtil.sum(taxBlank, tax.amount)
                    } else if (tax.code == ('ZZ') && (tax.amount != null)) {
                        if (includeZZTax) {
                            taxBlank = MiscUtil.sum(taxBlank, tax.amount)
                        }
                    }
                }
            }
        }
        if (ticket.getMcoCategory().equals(MCOCategory.PENALTY)
                && ticket.getPenaltyVPD() == null) {

            penalty = MiscUtil.sum(penalty, ticket.penalty)

        } else if (ticket.operationBatch == OperationBatch.SELL) {

            if (ticket.paymentTypes.contains(PaymentType.MTD)
                    || ticket.paymentTypes.contains(PaymentType.CREDIT)) {
                sellVPD = MiscUtil.sum(sellVPD, ticket.equivalentFare, taxAgs, taxBlank)
                rewardSellVPD = MiscUtil.sum(rewardSellVPD, ticket.vendorCommissionValue)
            } else if (ticket.mcoCategory == MCOCategory.EXCESS_LUGGAGE
                        || ticket.productCategory == ProductCategory.EXCESS_BAGAGE) {
                luggage = MiscUtil.sum(luggage, ticket.equivalentFare)
                rewardSell = MiscUtil.sum(rewardSell, ticket.vendorCommissionValue)
            } else {
                if (ticket.mcoCategory == MCOCategory.REBOOKING) {
                    sellPenalties = MiscUtil.sum(sellPenalties, ticket.equivalentFare)
                } else {
                    sellTariff = MiscUtil.sum(sellTariff, ticket.equivalentFare)
                }
                sellTaxAgs = MiscUtil.sum(sellTaxAgs, taxAgs)
                sellTaxBlank = MiscUtil.sum(sellTaxBlank, taxBlank)
                rewardSell = MiscUtil.sum(rewardSell, ticket.vendorCommissionValue)
            }

            amountTickets++

        } else if (ticket.operationBatch == OperationBatch.EXCHANGE) {

            if (ticket.paymentTypes.contains(PaymentType.MTD)
                    || ticket.paymentTypes.contains(PaymentType.CREDIT)) {
                sellVPD = MiscUtil.sum(sellVPD, ticket.equivalentFare, taxAgs, taxBlank)
                rewardSellVPD = MiscUtil.sum(rewardSellVPD, ticket.vendorCommissionValue)

            } else {
                if (ticket.mcoCategory == MCOCategory.REBOOKING) {
                    exchangePenalties = MiscUtil.sum(exchangePenalties, ticket.equivalentFare)
                } else {
                    exchangeAddTariff = MiscUtil.sum(exchangeAddTariff, ticket.equivalentFare)
                }
                exchangeTaxAgs = MiscUtil.sum(exchangeTaxAgs, taxAgs)
                exchangeTaxBlank = MiscUtil.sum(exchangeTaxBlank, taxBlank)
                rewardExchange = MiscUtil.sum(rewardExchange, ticket.vendorCommissionValue)
            }

        } else if (ticket.operationBatch == OperationBatch.REFUND) {

            if (ticket.paymentTypes.contains(PaymentType.MTD)
                    || ticket.paymentTypes.contains(PaymentType.CREDIT)) {

                refundVPD = MiscUtil.sum(refundVPD, ticket.equivalentFare, taxAgs, taxBlank)
                rewardRefundVPD = MiscUtil.sum(ticket.vendorCommissionValue, rewardRefundVPD)

            } else {
                if (ticket.mcoCategory == MCOCategory.REBOOKING) {
                    refundPenalties = MiscUtil.sum(refundPenalties, ticket.equivalentFare)
                } else {
                    refundAddTariff = MiscUtil.sum(refundAddTariff, ticket.equivalentFare)
                }
                refundTaxAgs = MiscUtil.sum(refundTaxAgs, taxAgs)
                refundTaxBlank = MiscUtil.sum(refundTaxBlank, taxBlank)
                rewardRefund = MiscUtil.sum(rewardRefund, ticket.vendorCommissionValue)
                penalty = MiscUtil.sum(penalty, ticket.penalty)
            }

            amountExchangeTickets++

        } else {
            amountVoidTickets++
        }

        totalPenlties = MiscUtil.sub(MiscUtil.sum(sellPenalties, exchangePenalties), refundPenalties)

        sell = MiscUtil.sum(sellTariff, sellTaxAgs, sellTaxBlank)
        exchange = MiscUtil.sum(exchangeAddTariff, exchangeTaxAgs, exchangeTaxBlank, totalPenlties)
        refund = MiscUtil.sub(MiscUtil.sum(refundAddTariff, refundTaxAgs, refundTaxBlank), penalty)

        rewardVDP = MiscUtil.sub(rewardSellVPD, rewardRefundVPD)
        reward = MiscUtil.sub(MiscUtil.sum(rewardSell, rewardVDP, rewardExchange), rewardRefund)
        totalToIrAero = MiscUtil.sub(MiscUtil.sum(sell, exchange, luggage), refund, reward)
    }

    result.add(sell)                    //0
    result.add(sellTariff)              //1
    result.add(sellTaxBlank)            //2
    result.add(sellTaxAgs)              //3
    result.add(sellVPD)                 //4

    result.add(exchange)                //5
    result.add(exchangeAddTariff)       //6
    result.add(exchangeTaxBlank)        //7
    result.add(exchangeTaxAgs)          //8

    result.add(refund)                  //9
    result.add(refundAddTariff)         //10
    result.add(refundTaxBlank)          //11
    result.add(refundTaxAgs)            //12
    result.add(penalty)                 //13
    result.add(refundVPD)               //14

    result.add(reward)                  //15
    result.add(rewardSell)              //16
    result.add(rewardExchange)          //17
    result.add(rewardRefund)            //18
    result.add(rewardVDP)               //19

    result.add(totalToIrAero)           //20
    result.add(amountTickets)           //21
    result.add(amountVoidTickets)       //22
    result.add(amountExchangeTickets)   //23
    result.add(luggage)                 //24
    result.add(totalPenlties)           //25
    return result
}

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

    def tableAk = []

    for(String key : map.keySet()) {
        tableAk = makeCalculation(map.get(key))
    }

    def dates = parameters.REPORT_PERIOD.split('-')


    table.add(['',    'Сальдо на ' + dates[0]                             , ''])
    table.add(['1',   'Выручка по реестрам продажи авибилетов'            , tableAk[0]])
    table.add(['1.1', 'Тариф'                                             , tableAk[1]])
    table.add(['1.2', 'Такса (YR)'                                        , tableAk[2]])
    table.add(['1.3', 'Такса АГС(YQ)'                                     , tableAk[3]])
    table.add(['1.4', 'По реестрам ВПД, Кредит'                           , tableAk[4]])
    table.add(['2',   'Выручка по реестрам обмена авиабилетов'            , tableAk[5]])
    table.add(['2.1', 'Доплата по тарифу'                                 , tableAk[6]])
    table.add(['2.2', 'Такса (YR)'                                        , tableAk[7]])
    table.add(['2.3', 'Доплата по таксе АГС(YQ)'                          , tableAk[8]])
    table.add(['2.4', 'Штрафы'                                            , tableAk[25]])
    table.add(['3',   'Багаж'                                             , tableAk[24]])
    table.add(['4',   'Груз'                                              , text('', 'emptyNumberData')])
    table.add(['5',   'Возврат'                                           , tableAk[9]])
    table.add(['5.1', 'Тариф'                                             , tableAk[10]])
    table.add(['5.2', 'Такса (YR)'                                        , tableAk[11]])
    table.add(['5.3', 'Такса АГС(YQ)'                                     , tableAk[12]])
    table.add(['5.4', 'Штраф'                                             , tableAk[13]])
    table.add(['5.5', 'По реестрам ВПД, Кредит'                           , tableAk[14]])
    table.add(['6',   'Комиссионное вознаграждение'                       , tableAk[15]])
    table.add(['6.1', 'По реестрам продажи'                               , tableAk[16]])
    table.add(['6.2', 'По реестрам обмена'                                , tableAk[17]])
    table.add(['6.3', 'По реестрам возврата'                              , tableAk[18]])
    table.add(['6.4', 'В том числе по реестрам ВПД, Кредит'               , tableAk[19]])
    table.add(['6.5', 'Суммы восстановления по замечаниям авиакомпании'   , text('', 'emptyNumberData')])
    table.add(['6.6', 'Штраф за аннулированные бланки'                    , text('', 'emptyNumberData')])
    table.add(['7', 'Итого к перечислению в а/к'                          , tableAk[20]])
    table.add(['8', 'Перечислено'                                         , text('', 'emptyNumberData')])
    table.add(['9', 'ИТОГО перечислено'                                   , text('', 'emptyNumberData')])
    table.add(['',  'Сальдо на ' + dates[1]                               , Formula.getSumFormulaByCell(cellIndex(1, 2), cellIndex(27, 2), '-' + cellIndex(28, 2))])
    table.add(['',  'Проданные БСО'                                       , tableAk[21]])
    table.add(['',  'Аннулированные БСО'                                  , tableAk[22]])
    table.add(['',  'Возвращенные БСО'                                    , tableAk[23]])

    return table
}

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

    table.each { row ->
        nextRow()
        rowHeight(12)

        row.each {
            cellFill(it)
            nextColumn()
        }
    }
}

def reportBody = {
    tableHeader()
    tableBody()
}

page{parameters['title']}{

    fitWidth(1)
    fitHeight(1)
    landscape(false)

    warn 'report version = ' + '0.1.0'

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

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