// Imports

import com.gridnine.xtrip.common.l10n.model.LocaleHelper
import com.gridnine.xtrip.common.l10n.model.LocaleManager
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.dict.CodeSystem
import com.gridnine.xtrip.common.model.dict.CommunicationType
import com.gridnine.xtrip.common.model.dict.DictionaryCache
import com.gridnine.xtrip.common.model.dict.GdsName
import com.gridnine.xtrip.common.model.dict.PassengerStatus
import com.gridnine.xtrip.common.model.dict.ProfileGroup
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.helpers.DictHelper
import com.gridnine.xtrip.common.model.helpers.PersonalLocalizableNameFormatter
import com.gridnine.xtrip.common.model.helpers.ProfileHelper
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.model.ClientFop
import com.gridnine.xtrip.common.model.profile.SalesPoint
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportTicket
import com.gridnine.xtrip.common.util.*
import com.gridnine.xtrip.common.model.system.Metadata

import java.text.DecimalFormat
import java.text.SimpleDateFormat

// Styles
createStyle(name: 'title', h_alignment: 'LEFT', v_alignment: 'CENTER', wrapText: true)
createStyle(name: 'titleH1', fontHeight: 12, parent: 'title')
createStyle(name: 'titleH2', fontHeight: 10, fontBold: true, parent: 'title')
createStyle(name: 'header', h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight: 8)
createStyle(name: 'columnHeader', fontBold: true, parent: 'header')
createStyle(name: 'rowHeader', parent: 'header')
createStyle(name: 'data', h_alignment: 'LEFT', v_alignment: 'CENTER', fontHeight: 8)
createStyle(name: 'dataText', parent: 'data')
createStyle(name: 'dataDate', format: 'm/d/yy', parent: 'data')
createStyle(name: 'dataNumber', h_alignment: 'RIGHT', parent: 'data')
createStyle(name: 'dataSum', format: '#,##0.00', parent: 'dataNumber')
createStyle(name: 'wrap', wrapText: true)
createStyle(name: 'ahl', h_alignment: 'LEFT')
createStyle(name: 'ahc', h_alignment: 'CENTER')
createStyle(name: 'ahr', h_alignment: 'RIGHT')
createStyle(name: 'avt', v_alignment: 'TOP')
createStyle(name: 'avc', v_alignment: 'CENTER')
createStyle(name: 'avb', v_alignment: 'BOTTOM')
createStyle(name: 'aac', h_alignment: 'CENTER', v_alignment: 'CENTER')
createStyle(name: 'bold', fontBold: true)
createStyle(name: 'italic', fontItalic: true)
createStyle(name: 'bt', topBorder: 'THIN')
createStyle(name: 'bl', leftBorder: 'THIN')
createStyle(name: 'bb', bottomBorder: 'THIN')
createStyle(name: 'br', rightBorder: 'THIN')
createStyle(name: 'ba', topBorder: 'THIN', leftBorder: 'THIN', bottomBorder: 'THIN', rightBorder: 'THIN')

// Closures
def period = {
    SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy")
    def periodBeginParameter = parameters['key-report-params']?.periodBegin
    def periodEndParameter = parameters['key-report-params']?.periodEnd
    return " за период с " +
            (periodBeginParameter ? sdf.format(periodBeginParameter) : "") +
            " по " +
            (periodEndParameter ? sdf.format(periodEndParameter) : "")
}

def agentName = {
    def person = EntityStorage.get().resolve(parameters['key-report-params']?.agent)?.entity
    def format = new PersonalLocalizableNameFormatter(PersonalLocalizableNameFormatter.SIMPLE)
    return person ? format.format(person, LocaleHelper.getLocale('ru', 'RU'), false) : '?'
}

def agentPhone = {
    def person = EntityStorage.get().resolve(parameters['key-report-params']?.agent)?.entity
    def communication = person ? ProfileHelper.filterPersonCommunications(person.getCommunications(), CommunicationType.WORK_PHONE, parameters['key-report-params']?.agency) : null
    return communication ? ProfileHelper.buildFullPhoneNumber(communication) : '?'
}

def accountantName = {
    def person = EntityStorage.get().resolve(requestedParameter('accountant'))?.entity
    def format = new PersonalLocalizableNameFormatter(PersonalLocalizableNameFormatter.SIMPLE)
    return person ? format.format(person, LocaleHelper.getLocale('ru', 'RU'), false) : '?'
}

def CEOName = {
    def person = EntityStorage.get().resolve(requestedParameter('director'))?.entity
    def format = new PersonalLocalizableNameFormatter(PersonalLocalizableNameFormatter.SIMPLE)
    return person ? format.format(person, LocaleHelper.getLocale('ru', 'RU'), false) : '?'
}

def fillMap = {
    Map<EntityReference<Organization>, Set<AirTicketsTemplateReportTicket>> ticketsMap = new HashMap()
    List<EntityReference<Organization>> clients = requestedParameter('clients')
    if (clients == null) {
        return ticketsMap
    }
    tickets { AirTicketsTemplateReportTicket ticket ->
        List<ClientFop> fops = ticket.getClientFops()
        if (fops != null) {
            for (EntityReference<Organization> client : clients){
                if (ticket.previousTicketNumber == null || ticket.status == ProductStatus.REFUND){
                    Set<AirTicketsTemplateReportTicket> ticketSet = ticketsMap.get(client)
                    if (ticketSet == null) {
                        ticketSet = new LinkedHashSet<>()
                        ticketsMap.put(client, ticketSet)
                    }
                    for (ClientFop fop : fops) {
                        if (client == fop.payer) {
                            ticketSet.add(ticket)
                        }
                    }
                }
            }
        }
    }
    return ticketsMap
}

//Functions
def cashierPersonalNumber(agent) {
    def result = 'Не указано'
    def cashier = EntityStorage.get().resolve(agent)?.entity
    if (cashier != null) {
        cashier.getGdsReferences().each {
            if (it.getGdsName() == GdsName.SIRENA)
                result = it.getReference()
        }
    }
    return result
}

def isRefund(ticket) {
    return OperationBatch.REFUND == ticket?.operationBatch
}

def amountOfPayers(AirTicketsTemplateReportTicket ticket, EntityReference<Organization> org) {
    BigDecimal amount = BigDecimal.ZERO
    ticket.clientFops.each { ClientFop fop ->
        if (fop.payer.equals(org)){
            if (PassengerStatus.VS_FSB == fop.passengerStatus && fop.commissions.isEmpty()){
                amount = MiscUtil.sum(amount, fop.amount.value)
            }
        }
    }
    return amount
}

def amountOfFees(AirTicketsTemplateReportTicket ticket, EntityReference<Organization> org) {
    BigDecimal amount = BigDecimal.ZERO
    ticket.getClientFops().each { ClientFop fop ->
        if (fop.payer.equals(org)){
            if (PassengerStatus.VS_FSB == fop.passengerStatus && !fop.commissions.isEmpty()){
                amount = MiscUtil.sum(amount, fop.amount.value)
            }
        }
    }
    return amount
}

def nextRowWithHeight(height) {
    rowHeight(height)
    nextRow()
}

def printFee(ticket, counter, client) {
    nextRowWithHeight(25)
    number(++counter, 'dataText|ba')
    nextColumn()
    text(cashierPersonalNumber(ticket.getAgent()), 'dataText|ba')
    nextColumn()
    text(ticket.militaryClaimNumber ? ticket.militaryClaimNumber : "", 'dataText|ba')
    nextColumn()
    text((ticket.validatingCarrierNumber ? ticket.validatingCarrierNumber + " " : "") +
            (ticket.ticketNumber ? ticket.ticketNumber : ""), 'dataText|ba')
    nextColumn()
    text(ticket.validatingCarrier ? DictHelper.getCodeVariant(ticket.validatingCarrier, CodeSystem.CRT) : "", 'dataText|ba')
    nextColumn()
    date(ticket.issueDate, 'dataDate|ba')
    nextColumn()
    date(ticket.departureDate, 'dataDate|ba')
    nextColumn()
    text("", 'dataText|ba')
    nextColumn()
    number(isRefund(ticket) ? BigDecimal.ZERO : amountOfFees(ticket, client), 'dataSum|ba')
    nextColumn()
    number(isRefund(ticket) ? amountOfFees(ticket, client) : BigDecimal.ZERO, 'dataSum|ba')
    2.times {
        nextColumn()
        text("", 'dataText|ba')
    }
}

//Main loop
Map<EntityReference<Organization>, Set<AirTicketsTemplateReportTicket>> ticketsMap = fillMap()
requestedParameter('clients').forEach() { EntityReference<Organization> client ->
    Map<String, ProfileGroup> clientGroupList = DictionaryCache.get().getAll(ProfileGroup.class)
    Organization prodClient = EntityStorage.get().resolve(client)?.entity
    Set<AirTicketsTemplateReportTicket> ticketsSet = ticketsMap.get(client) ?: new LinkedHashSet<>()
    Map<String, List<AirTicketsTemplateReportTicket>> ticketsSortedByNameMap = new HashMap<>()

    ticketsSet.forEach() { AirTicketsTemplateReportTicket ticket ->

        SalesPoint ticketSalesePoint = EntityStorage.get().resolve(ticket.getSalesPoint())?.entity
        List<Metadata> ticketGroupList = ticketSalesePoint.getMetadata()

        ticketGroupList.each {Metadata tm ->
            groupref = tm.value
            clientGroupList.each { String s, ProfileGroup group ->
                if (groupref == group.toReference()){
                    String groupName = group.translations.get(LocaleManager.get().getCurrentLocale()) ?: ''
                    if (!groupName.equals('') || groupName != null) {
                        group.translations.findAll { Locale l, String name ->
                            if (!name.equals('') || name != null) {
                                groupName = name
                            }
                        }
                    }
                    List<AirTicketsTemplateReportTicket> list =
                            ticketsSortedByNameMap.get(groupName)
                    if (list == null){
                        list = new ArrayList<>()
                        ticketsSortedByNameMap.put(groupName, list)
                    }
                    list.add(ticket)
                }
            }
        }
    }


    BigDecimal sumTickets = BigDecimal.ZERO
    BigDecimal sumFee = BigDecimal.ZERO

    BigDecimal totalSumTickets = BigDecimal.ZERO
    BigDecimal totalSumTicketsRef = BigDecimal.ZERO

    Set mtdSet = new HashSet()

    page { 'Реестр ' + prodClient.getShortName().toString().replaceAll("\\d*\\*", "") } {
        // Set landscape mode
        landscape(true)

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

        // Set scale
        scale(100)

        // Set preserve mode
        preserve(false)

// Report header
        text('Реестр перевозочных документов по требованиям ' +
                prodClient.shortName + period(), 'title|ba|avb', 12, 1)
        nextRowWithHeight(40)

// Table header
        text('№', 'columnHeader|wrap|ba')
        nextColumn()
        text('Касса', 'columnHeader|wrap|ba')
        nextColumn()
        text('№ документа', 'columnHeader|wrap|ba')
        nextColumn()
        text('Тип, серия, № а/б', 'columnHeader|wrap|ba')
        nextColumn()
        text('А/К', 'columnHeader|wrap|ba')
        nextColumn()
        text('Дата продажи', 'columnHeader|wrap|ba')
        nextColumn()
        text('Дата вылета', 'columnHeader|wrap|ba')
        nextColumn()
        text('Маршрут', 'columnHeader|wrap|ba')
        nextColumn()
        text('Продажа', 'columnHeader|wrap|ba')
        nextColumn()
        text('Возврат', 'columnHeader|wrap|ba')
        nextColumn()
        text('ФИО пассажира', 'columnHeader|wrap|ba')
        nextColumn()
        text('№ удостоверения', 'columnHeader|wrap|ba')

        nextRowWithHeight(25)

// Table
        int counter = 0
        int amountTickets
        ticketsSortedByNameMap.forEach() { String name, List<AirTicketsTemplateReportTicket> sortedTicketsList ->
            if (!sortedTicketsList.isEmpty() || sortedTicketsList != null) {
                rowHeight(20)
                text(name, 'titleH2|ahl', 12 ,1)
                nextRow()
                amountTickets = 0
                sortedTicketsList.forEach() {
                    amountTickets++
                    number(++counter, 'dataText|ba')
                    nextColumn()
                    text(cashierPersonalNumber(it.getAgent()), 'dataText|ba')
                    nextColumn()
                    text(it.militaryClaimNumber ? it.militaryClaimNumber : "", 'dataText|ba')
                    nextColumn()
                    text((it.validatingCarrierNumber ? it.validatingCarrierNumber + " " : "") +
                            (it.ticketNumber ? it.ticketNumber : ""), 'dataText|ba')
                    nextColumn()
                    text(it.validatingCarrier ? DictHelper.getCodeVariant(it.validatingCarrier, CodeSystem.CRT) : "", 'dataText|ba')
                    nextColumn()
                    date(it.issueDate, 'dataDate|ba')
                    nextColumn()
                    date(it.departureDate, 'dataDate|ba')
                    nextColumn()
                    text(it.crtRouteLine, 'dataText|ba')
                    nextColumn()
                    number(isRefund(it) ? BigDecimal.ZERO : amountOfPayers(it, client), 'dataSum|ba')
                    nextColumn()
                    number(isRefund(it) ? amountOfPayers(it, client) : BigDecimal.ZERO, 'dataSum|ba')
                    nextColumn()
                    text(it.travellerName ? it.travellerName : "", 'dataText|ba')
                    nextColumn()
                    text(it.travellerPassportNumber ? it.travellerPassportNumber : "", 'dataText|ba')

                    if (it.militaryClaimNumber != null && !isRefund(it)) {
                        mtdSet.add(it.militaryClaimNumber)
                    }

                    if (it.price != null && !isRefund(it)) {
                        sumTickets = sumTickets.add(amountOfPayers(it, client))
                        totalSumTickets = totalSumTickets.add(amountOfPayers(it, client))
                    }

                    if (it.price != null && isRefund(it)) {
                        totalSumTicketsRef = totalSumTicketsRef.add(amountOfPayers(it, client))
                    }

                    if (it.clientFeeValue != null && !isRefund(it)) {
                        totalSumTickets = totalSumTickets.add(amountOfFees(it, client))
                    }

                    if (it.clientFeeValue != null && isRefund(it)) {
                        totalSumTicketsRef = totalSumTicketsRef.add(amountOfFees(it, client))
                    }

                    if (it.clientFeeValue != null) {
                        printFee(it, counter, client)
                        sumFee = sumFee.add(amountOfFees(it, client))
                        counter++
                        amountTickets++
                    }

                    nextRowWithHeight(25)
                }
            }

//Summary block
            rowHeight(20)
            2.times {
                text('', 'dataText|ba')
                nextColumn()
            }
            text('ИТОГО', 'dataText|ba|avb|bold')
            6.times {
                nextColumn()
                text('', 'dataText|ba')
            }
            if (!sortedTicketsList.isEmpty()) {
                formula("SUM(${cellIndex(-amountTickets, 0)}:${cellIndex(-1, 0)})", 'dataSum|ba|avb|bold')

                nextColumn()
                formula("SUM(${cellIndex(-amountTickets, 0)}:${cellIndex(-1, 0)})", 'dataSum|ba|avb|bold')

            } else {
                number(BigDecimal.ZERO, 'dataSum|ba|avb|bold')
                nextColumn()
                number(BigDecimal.ZERO, 'dataSum|ba|avb|bold')
            }

            2.times {
                nextColumn()
                text('', 'dataText|ba')
            }
            nextRow()
        }

//Total summary block
        rowHeight(30)
        2.times {
            text('', 'dataText|bb')
            nextColumn()
        }
        text('ИТОГО', 'dataText|bb|avb|bold')
        6.times {
            nextColumn()
            text('', 'dataText|bb')
        }
        if (!ticketsSortedByNameMap.isEmpty()) {
            number(totalSumTickets, 'dataSum|bb|avb|bold')
            nextColumn()
            number(totalSumTicketsRef, 'dataSum|bb|avb|bold')
        } else {
            number(BigDecimal.ZERO, 'dataSum|bb|avb|bold')
            nextColumn()
            number(BigDecimal.ZERO, 'dataSum|bb|avb|bold')
        }

        2.times {
            nextColumn()
            text('', 'dataText|bb')
        }
        nextRow()

        2.times { nextColumn() }
        text('Кол-во ВПД ' + mtdSet.size() + ' шт.', 'dataText')
        nextRow()

        // Auto width
        15.times {
            columnAutoWidth()
            nextColumn()
        }
        nextRow()

        //Manual column width
        7.times { nextColumn() }
        columnWidth(20)
        nextColumn()
        columnWidth(20)
        nextColumn()
        columnWidth(20)
        nextRow()

        //Credits

        2.times { nextRow() }
        4.times { nextColumn() }
        text('Генеральный директор:', 'titleH2|ahl', 3, 1)
        3.times { nextColumn() }
        text('', 'bb')
        nextColumn()
        text(CEOName(), 'titleH2|ahl')

        2.times { nextRow() }
        4.times { nextColumn() }
        text('Главный бухгалтер:', 'titleH2|ahl', 3, 1)
        3.times { nextColumn() }
        text('', 'bb')
        nextColumn()
        text(accountantName(), 'titleH2|ahl')

        2.times { nextRow() }
        4.times { nextColumn() }
        def signPlace = '  _______________________'
        text('Представитель ' + prodClient.shortName + signPlace, 'titleH2|ahl', 8, 1)

        2.times { nextRow() }
        text('Исполнил: ', 'dataText', 2, 1)
        2.times { nextColumn() }
        text(agentName(), 'dataText')
        nextRow()
        text('тел./факс', 'dataText', 2, 1)
        2.times { nextColumn() }
        text(agentPhone(), 'dataText')

        //Total footer
        3.times { nextRow() }
        text('Cумма по билетам ', 'dataText|bold', 4, 1)
        4.times { nextColumn() }
        text(new DecimalFormat("#,##0.00").format(sumTickets) + 'p.', 'dataSum|bold|ahl', 2, 1)
        nextRow()
        text('Плата за оформление авиаперевозки ', 'dataText|bold', 4, 1)
        4.times { nextColumn() }
        text(new DecimalFormat("#,##0.00").format(sumFee) + 'p.', 'dataSum|bold|ahl', 2, 1)
    }
}