import com.gridnine.xtrip.common.l10n.model.LocaleHelper
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.AddressType
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.helpers.DictHelper
import com.gridnine.xtrip.common.model.helpers.ProfileHelper
import com.gridnine.xtrip.common.model.profile.Address
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.model.system.PaymentType
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportTicket
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportTicketType
import com.gridnine.xtrip.common.util.MiscUtil

import java.text.SimpleDateFormat
import java.time.Month

createStyle(name: "left", h_alignment: "LEFT")
createStyle(name: "left-italic", h_alignment: "LEFT", fontItalic : true)
createStyle(name: "right-italic", h_alignment: "RIGHT", fontItalic : true)
createStyle(name: "center", h_alignment: "CENTER")
createStyle(name: "left-bold", h_alignment: "LEFT", fontBold : true)
createStyle(name: "left-bold-italic", h_alignment: "LEFT", fontBold : true, fontItalic : true)
createStyle(name: "right-bold", h_alignment: "RIGHT", fontBold : true)
createStyle(name: "center-bold", h_alignment: "CENTER", fontBold : true)
createStyle(name: "center-center-bold", h_alignment: "CENTER", v_alignment: "CENTER", fontBold : true, fontName : "Arial", size : 12)
createStyle(name: "left-bold-border", h_alignment: "LEFT", fontBold : true, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "right-bold-border", h_alignment: "RIGHT", fontBold : true, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "center-bold-border", h_alignment: "CENTER", fontBold : true, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "center-bold-border-format", h_alignment: "CENTER", fontBold : true, format: '#,##0.00', leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "center-center-bold-border", h_alignment: "CENTER", v_alignment: "CENTER", fontBold : true, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "center-center-bold-medium_left_border", h_alignment: "CENTER", v_alignment: "CENTER", fontBold : true, leftBorder:'MEDIUM', rightBorder:'THIN', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
createStyle(name: "center-center-bold-medium_right_border", h_alignment: "CENTER", v_alignment: "CENTER", fontBold : true, leftBorder:'THIN', rightBorder:'MEDIUM', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
createStyle(name: "left-center-bold-medium_left_border", h_alignment: "LEFT", v_alignment: "CENTER", fontBold : true, leftBorder:'MEDIUM', rightBorder:'THIN', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
createStyle(name: "left-center-bold-italic-medium_left_border", h_alignment: "LEFT", v_alignment: "CENTER", fontBold : true, fontItalic : true, leftBorder:'MEDIUM', rightBorder:'THIN', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
createStyle(name: "center-center-bold-medium_right_border", h_alignment: "CENTER", v_alignment: "CENTER", fontBold : true, leftBorder:'THIN', rightBorder:'MEDIUM', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
createStyle(name: "center-bold-big", h_alignment: "CENTER", fontBold : true, fontHeight : 12)
createStyle(name: "left-center-border", h_alignment: "LEFT", v_alignment: "CENTER", leftBorder:'MEDIUM', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "right-border", h_alignment: "RIGHT", leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "center-border", h_alignment: "CENTER", leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "center-border-format", h_alignment: "CENTER", format: '#,##0.00', leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "right-center-border-number", format: '0.00', h_alignment: "RIGHT", v_alignment: "CENTER", leftBorder:'THIN', rightBorder:'MEDIUM', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: "right-center-bold-medium_border-number", format: '0.00', h_alignment: "RIGHT", v_alignment: "CENTER", fontBold : true, leftBorder:'THIN', rightBorder:'MEDIUM', topBorder:'MEDIUM', bottomBorder:'MEDIUM', wrapText: true)
//format: '0.00'
def printTable() {
    Map<ProductStatus, List<AirTicketsTemplateReportTicket>> statusToTickets = collectTickets()
    printTitle()
    printHeader()
    printTableContent(statusToTickets)
    printSignatures()
}

def printTitle() {
    EntityReference<Organization> agencyParameter = parameters['key-report-params']?.agency
    Organization organization = EntityStorage.get().resolve(agencyParameter)?.entity
    Address address = ProfileHelper.filterAddresses(organization?.getAddresses(), AddressType.LEGAL);

    text("РАСЧЕТНОЕ ПИСЬМО", "center-center-bold", 2, 1)

    nextRow()
    text("к финансовому отчету АО «Авиакомпания SCAT»", "center-center-bold", 2, 1)

    nextRow()
    nextRow()
    text("Агент: ", "left-bold")
    nextColumn()
    text(parameters['AGENCY_NAME'], "left-bold")

    nextRow()
    text("На дату:", "left-bold")
    nextColumn()
    text(getPeriod(), "left-bold")

    nextRow()
    text("Страна резидентства:", "left-bold")
    nextColumn()
    text(address?.getCountry() ? address.getCountry().toString(LocaleHelper.RU_LOCALE) : "", "left-bold-italic")

    nextRow()
    text("Город:", "left-bold")
    nextColumn()
    text(address?.getCity() ? address.getCity().toString(LocaleHelper.RU_LOCALE) : "", "left-bold-italic")

    nextRow()
    nextRow()
    nextColumn()
    text("Валюта расчетов  " + DictHelper.getLocalCurrencyCode(), "right-italic")
}

def printHeader() {
    nextRow()
    rowHeight(30)
    text("Раздел", "center-center-bold-medium_left_border")

    nextColumn()
    text("Сумма перевозчика", "center-center-bold-medium_right_border")
}

def printTableContent(Map<ProductStatus, List<AirTicketsTemplateReportTicket>> statusToTickets) {
    printBeginPeriodBalance()
    printSalesSubTable(statusToTickets)
    printSalesOthersSubTable()
    printRefundSubTable(statusToTickets)
    nextRow()
    printCommissionsSubTable(statusToTickets)
    nextRow()
    printTotal()
    nextRow()
    printPaymentsSubTable(statusToTickets)
    printPenaltiesSubTable()
    printEndPeriodBalance()
}

def printBeginPeriodBalance() {
    nextRow()
    rowHeight(30, false)
    SimpleDateFormat sdfBegin = new SimpleDateFormat("\"dd\" MM yyyy г.")
    Date periodBeginParameter = parameters['key-report-params']?.periodBegin
    String periodBegin = periodBeginParameter ? sdfBegin.format(periodBeginParameter) : ""
    text("Сальдо на " + periodBegin + "\n\t\tв пользу Авиакомпании составляет", "left-center-bold-italic-medium_left_border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-bold-medium_border-number")
}

def printSalesSubTable(
        Map<ProductStatus, List<AirTicketsTemplateReportTicket>> statusToTickets) {

    List<AirTicketsTemplateReportTicket> tickets = new ArrayList<>()
    tickets.addAll(statusToTickets.get(ProductStatus.SELL))
    tickets.addAll(statusToTickets.get(ProductStatus.EXCHANGE))

    nextRow()
    rowHeight(30, false)
    text("1. Выручка по реестрам продажи авиабилетов" + "\n" + "(1=1.1+1.2+1.3+1.4)"
            , "left-center-bold-medium_left_border")

    nextColumn()
    formula("SUM(${cellIndex(1, 0)}:${cellIndex(4, 0)})", "right-center-bold-medium_border-number")

    nextRow()
    text("1.1 - Тариф", "left-center-border")
    nextColumn()
    number(getTariff(tickets), "right-center-border-number")

    nextRow()
    text("1.2 - Сумма сборов", "left-center-border")
    nextColumn()
    number(getTaxesSum(tickets), "right-center-border-number")

    nextRow()
    text("1.3 - Штрафы за перебронирование", "left-center-border")
    nextColumn()
    number(getPenalty(tickets), "right-center-border-number")

    nextRow()
    text("1.4 - ", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")
}

def printSalesOthersSubTable() {
    nextRow()
    rowHeight(30, false)
    text("2. Выручка от продажи груза, багажа, почты" + "\n" + "(2=2.1+2.2+2.3+2.4)",
            "left-center-bold-medium_left_border")
    nextColumn()
    formula("SUM(${cellIndex(1, 0)}:${cellIndex(4, 0)})", "right-center-bold-medium_border-number")

    nextRow()
    text("2.1 — Выручка за груз", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    nextRow()
    text("2.2 — Выручка от продажи почты", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    nextRow()
    text("2.3 — Выручка за сверхнормативный багаж", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    nextRow()
    text("2.4 - ", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")
}

def printRefundSubTable(Map<ProductStatus, List<AirTicketsTemplateReportTicket>> statusToTickets) {

    List<AirTicketsTemplateReportTicket> tickets = statusToTickets.get(ProductStatus.REFUND)

    nextRow()
    rowHeight(30, false)
    text("3. Сумма по реестру возврата и перебронирования" + "\n" + "(3=3.1+3.2-3.3)",
            "left-center-bold-medium_left_border")

    nextColumn()
    formula("${cellIndex(1, 0)}+${cellIndex(2, 0)}-${cellIndex(3, 0)}", "right-center-bold-medium_border-number")

    nextRow()
    text("3.1 - Тариф", "left-center-border")
    nextColumn()
    number(getTariff(tickets), "right-center-border-number")

    nextRow()
    text("3.2 - Сборы", "left-center-border")
    nextColumn()
    number(getTaxesSum(tickets), "right-center-border-number")

    nextRow()
    text("3.3 - Штрафы и прочие сборы", "left-center-border")
    nextColumn()
    number(getPenalty(tickets), "right-center-border-number")

    nextRow()
    text("3.4 - Иное", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    printEmptyRow(1);
}

def printCommissionsSubTable(
        Map<ProductStatus, List<AirTicketsTemplateReportTicket>> statusToTickets) {

    rowHeight(30, false)
    text("4. Комиссионные агента (4=4.1+4.2+4.3+4.4-4.5)",
            "left-center-bold-medium_left_border")
    nextColumn()
    def vendorCommissionValueSell = getVendorCommissionValue(statusToTickets.get(ProductStatus.SELL))
    def vendorCommissionValueRefund = getVendorCommissionValue(statusToTickets.get(ProductStatus.REFUND))
    def vendorCommissionValueExchange = getVendorCommissionValue(statusToTickets.get(ProductStatus.EXCHANGE))
    formula("SUM(${cellIndex(1, 0)}:${cellIndex(4, 0)})-${cellIndex(5, 0)}", "right-center-bold-medium_border-number")

    nextRow()
    text("4.1 — Комиссия от продажи авиабилетов", "left-center-border")
    nextColumn()
    number(MiscUtil.sum(vendorCommissionValueSell, vendorCommissionValueExchange), "right-center-border-number")

    nextRow()
    text("4.2 — Комиссия за реализацию груза", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    nextRow()
    text("4.3 — Комиссия за реализацию почты", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    nextRow()
    text("4.4 — Комиссия за оформление сверхн. багажа", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    nextRow()
    text("4.5 — Возврат комиссионного награждения", "left-center-border")
    nextColumn()
    number(vendorCommissionValueRefund, "right-center-border-number")

    nextRow()
    text("4.6 — ", "left-center-border")
    nextColumn()
    number(BigDecimal.ZERO, "right-center-border-number")

    printEmptyRow(1);
}

def printTotal() {
    rowHeight(30, false)
    text("5. ИТОГО подлежит перечислению перевозчику (5=1+2-3-4)",
            "left-center-bold-medium_left_border")
    nextColumn()
    formula("${cellIndex(-24, 0)}+${cellIndex(-19, 0)}-${cellIndex(-14, 0)}-${cellIndex(-8, 0)}",
            "right-center-bold-medium_border-number")

    printEmptyRow(1);
}

def printPaymentsSubTable(Map<ProductStatus, List<AirTicketsTemplateReportTicket>> statusToTickets) {
    List<AirTicketsTemplateReportTicket> tickets = new ArrayList<>()
    tickets.addAll(statusToTickets.get(ProductStatus.SELL))
    tickets.addAll(statusToTickets.get(ProductStatus.EXCHANGE))
    List<AirTicketsTemplateReportTicket> ticketsRefund = statusToTickets.get(ProductStatus.REFUND)

    rowHeight(30, false)
    text("ОПЛАТА (Форма оплаты)", "left-center-bold-medium_left_border")
    nextColumn()
    formula("SUM(${cellIndex(1, 0)}:${cellIndex(2, 0)})",
            "right-center-bold-medium_border-number")

    nextRow()
    text("В счет взаиморасчетов", "left-center-border")
    nextColumn()
    number(getPaymentByType(tickets, PaymentType.INVOICE)
            .subtract(getPaymentByType(ticketsRefund, PaymentType.INVOICE)), "right-center-border-number")

    nextRow()
    text("Оплата наличными", "left-center-border")
    nextColumn()
    number(getPaymentByType(tickets, PaymentType.CASH)
            .subtract(getPaymentByType(ticketsRefund, PaymentType.CASH)), "right-center-border-number")

    5. times {
        nextRow()
        text("По платежному поручению №_______от", "left-center-border")
        nextColumn()
        text("", "right-center-border-number")
    }
}

def printPenaltiesSubTable() {
    nextRow()
    rowHeight(30, false)
    text("Штрафы", "left-center-bold-medium_left_border")
    nextColumn()
    number(getAgencyMemoPenalties(), "right-center-bold-medium_border-number")

    nextRow()
    text("Штраф за аннулированные бланки", "left-center-border")
    nextColumn()
    text("", "right-center-border-number")


    nextRow()
    text("Несвоевременное предоставление отчета", "left-center-border")
    nextColumn()
    text("", "right-center-border-number")

    nextRow()
    text("Несвоевременное предоставление купонов", "left-center-border")
    nextColumn()
    text("", "right-center-border-number")

    printEmptyRow(4);
}

def printEndPeriodBalance() {
    nextRow()
    rowHeight(30, false)
    SimpleDateFormat sdfEnd = new SimpleDateFormat("\"dd\" MM yyyy г.")
    Date periodEndParameter = parameters['key-report-params']?.periodEnd
    String periodEnd = periodEndParameter ? sdfEnd.format(periodEndParameter) : ""
    text("Сальдо на " + periodEnd + "\n\t\tв пользу Авиакомпании составляет", "left-center-bold-italic-medium_left_border")
    nextColumn()
    formula("${cellIndex(-43, 0)}+${cellIndex(-18, 0)}-${cellIndex(-16, 0)}+${cellIndex(-8, 0)}",
            "right-center-bold-medium_border-number")
}

def printSignatures() {
    nextRow(); nextRow(); nextRow();
    text("От Агента:  " + parameters['AGENCY_NAME'], "left-bold-italic")
    nextColumn()
    text("От Авиакомпании SCAT:", "left-bold-italic")

    nextRow()
    nextRow()
    text("Отчет составлен  \"  \"\t\t20\tг.", "left")
    nextColumn()
    text("Отчет проверен  \"  \"\t\t20\tг.", "left")

    nextRow(); nextRow()
    text("Руководитель_______________", "left-italic")

    nextRow(); nextRow()

    text("Бухгалтер_______________", "left-italic")
    nextColumn()
    text("Бухгалтер_______________", "left-italic")

    nextRow()
    text("М.П.", "left")
    nextColumn()
    text("М.П.", "left")
}

def printEmptyRow(int count) {
    count.times {
        nextRow()
        text("", "left-center-border")
        nextColumn()
        text("", "right-center-border-number")
    }
}

def collectTickets() {
    Map<ProductStatus, List<AirTicketsTemplateReportTicket>> statusToTickets = new EnumMap<>(ProductStatus.class)
    statusToTickets.put(ProductStatus.SELL, new ArrayList<AirTicketsTemplateReportTicket>())
    statusToTickets.put(ProductStatus.EXCHANGE, new ArrayList<AirTicketsTemplateReportTicket>())
    statusToTickets.put(ProductStatus.REFUND, new ArrayList<AirTicketsTemplateReportTicket>())

    tickets { AirTicketsTemplateReportTicket ticket ->
        if (ticket.getType() != AirTicketsTemplateReportTicketType.MEMO
                && ticket.operationBatch != null
                && ticket.operationBatch != OperationBatch.VOID) {

            ArrayList<AirTicketsTemplateReportTicket> tickets
            switch (ticket.operationBatch) {
                case OperationBatch.SELL:
                    tickets = statusToTickets.get(ProductStatus.SELL)
                    break
                case OperationBatch.REFUND:
                    tickets = statusToTickets.get(ProductStatus.REFUND)
                    break
                case OperationBatch.EXCHANGE:
                    tickets = statusToTickets.get(ProductStatus.EXCHANGE)
                    break
                default:
                    throw new IllegalArgumentException("Unsupported batch: " + ticket.operationBatch)
            }
            tickets.add(ticket)
        }
    }
    return statusToTickets
}

static def getTariff(List<AirTicketsTemplateReportTicket> ticketsList) {
    BigDecimal res = BigDecimal.ZERO

    ticketsList.each { AirTicketsTemplateReportTicket ticket ->
        if (ticket.equivalentFare != null) {
            res = res.add(ticket.equivalentFare)
        }
        ticket.paymentTypes
    }

    return res
}

static def getPaymentByType(List<AirTicketsTemplateReportTicket> ticketsList, PaymentType paymentType) {
    BigDecimal res = BigDecimal.ZERO

    ticketsList.each { AirTicketsTemplateReportTicket ticket ->
            BigDecimal amount = ticket.getVendorFops().stream()
                .filter({fop -> fop.getType() == paymentType})
                .map({ fop -> fop.getAmount()})
                .filter({amount -> amount != null})
                .map({ amount -> amount.getValue()})
                .filter({value -> value != null})
                .reduce(BigDecimal.ZERO, { v1, v2 -> v1.add(v2) })

            res = res.add(amount)
    }

    return res
}

static def getPenalty(List<AirTicketsTemplateReportTicket> ticketsList) {
    BigDecimal res = BigDecimal.ZERO

    ticketsList.each { AirTicketsTemplateReportTicket ticket ->
        if (ticket.penalty != null) {
            res = res.add(ticket.penalty)
        }
    }

    return res
}

static def getTaxesSum(List<AirTicketsTemplateReportTicket> ticketsList) {
    BigDecimal res = BigDecimal.ZERO

    ticketsList.each { AirTicketsTemplateReportTicket ticket ->
        if (ticket.taxesSum != null) {
            res = res.add(ticket.taxesSum)
        }
    }

    return res
}

static def getVendorCommissionValue(List<AirTicketsTemplateReportTicket> ticketsList) {
    BigDecimal res = BigDecimal.ZERO

    ticketsList.each { AirTicketsTemplateReportTicket ticket ->
        if (ticket.vendorCommissionValue != null) {
            res = res.add(ticket.vendorCommissionValue)
        }
    }

    return res
}

def getAgencyMemoPenalties() {
    BigDecimal res = BigDecimal.ZERO

    tickets { AirTicketsTemplateReportTicket ticket ->
        if (ticket.type == AirTicketsTemplateReportTicketType.MEMO) {
            res = MiscUtil.sum(res,
                 ticket.agencyMemoProductTariffEquivalentAmount,
                 ticket.agencyMemoProductTaxesCarrierEquivalentAmount,
                 ticket.agencyMemoProductTaxesFuelEquivalentAmount,
                 ticket.agencyMemoProductTaxesStateEquivalentAmount,
                 ticket.agencyMemoProductTaxesOthersEquivalentAmount,
                 ticket.agencyMemoProductPenaltyEquivalentAmount,
                 ticket.agencyMemoProductContractPenaltyEquivalentAmount,
                 ticket.agencyMemoProductCommissionEquivalentAmount,
                 ticket.agencyMemoProductBonusEquivalentAmount,
                 ticket.agencyMemoProductFeeEquivalentAmount)
        }
    }

    return res
}

def getPeriod() {
    StringBuilder stringBuilder = new StringBuilder();
    Calendar calendar = Calendar.getInstance();
    SimpleDateFormat sdfBegin = new SimpleDateFormat("dd.MM.yyyy")
    SimpleDateFormat sdfEnd = new SimpleDateFormat("dd.MM.yyyy")

    Date periodBeginParameter = parameters['key-report-params']?.periodBegin
    Date periodEndParameter = parameters['key-report-params']?.periodEnd

    Integer yearBegin = MiscUtil.getYear(periodBeginParameter)
    Integer yearEnd = MiscUtil.getYear(periodEndParameter)

    Integer monthBegin = null
    if (periodBeginParameter) {
        calendar.setTime(periodBeginParameter)
        monthBegin = calendar.get(Calendar.MONTH)
    }
    Integer monthEnd = null
    if (periodEndParameter) {
        calendar.setTime(periodEndParameter)
        monthEnd = calendar.get(Calendar.MONTH)
    }

    if (yearBegin == yearEnd) {
        sdfBegin = new SimpleDateFormat("dd.MM")
        if (monthBegin == monthEnd) {
            sdfBegin = new SimpleDateFormat("dd")
        }
    }
    stringBuilder.append(periodBeginParameter ? sdfBegin.format(periodBeginParameter) + " - " : "")
    stringBuilder.append(periodEndParameter ? sdfEnd.format(periodEndParameter) : "")

    return stringBuilder.toString()
}

def setAutoWidth() {
    nextRow()
    rowAutoHeight()

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

page{"РП " + getPeriod()} {
    // Set fix width for amount of sheets
    fitWidth(1)

    // Set fix height for amount of sheets
    fitHeight(1)

    // Set portrait mode
    landscape(false)

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

    // Set scale
    scale(70)

    // Set preserve mode
    preserve(false)

    printTable()
    setAutoWidth()
}