import com.gridnine.xtrip.common.Environment
import com.gridnine.xtrip.common.gds.model.DailySalesReportItem
import com.gridnine.xtrip.common.gds.model.DailySalesReportParameters
import com.gridnine.xtrip.common.midoffice.model.SalesContext
import com.gridnine.xtrip.common.model.Xeption
import com.gridnine.xtrip.common.model.booking.OperationBatch
import com.gridnine.xtrip.common.model.booking.ProductIndex
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.Traveller
import com.gridnine.xtrip.common.model.dict.GdsName
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.ibecorp.DqbReportSettings
import com.gridnine.xtrip.common.parsers.model.IBusParsersContextKeys
import com.gridnine.xtrip.common.util.MiscUtil
import com.gridnine.xtrip.common.util.Pair
import com.gridnine.xtrip.common.util.UUIDGenerator
import com.gridnine.xtrip.server.ibus.IntegrationBusFacade
import com.gridnine.xtrip.server.ibus.components.MessageContext
import com.gridnine.xtrip.server.parsers2.common.TravellerDataProcessor
import com.gridnine.xtrip.server.reports.prepare.PrepareReportCallback
import com.gridnine.xtrip.server.reports.prepare.ServerReportHelper
import org.apache.commons.lang.StringUtils

createStyle(name: 'title',fontBold: true, h_span: 6, 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 : 'subHeader', fontHeight: 15, parent: 'title')
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')


def es = EntityStorage.get();
PrepareReportCallback callback = parameters[ServerReportHelper.KEY_PREPARE_REPORT_CALLBACK];
def settingsCtr = es.load(DqbReportSettings.class, "dqb-report-settings");
if(!settingsCtr){
    throw Xeption.forAdmin("в базе отсутствуют настройки DQB отчета");
}
if(settingsCtr.getEntity().getPccEntries().isEmpty()){
    throw Xeption.forAdmin("в настройках DQB отчета не заполнены данные по PCC");
}
for(def entry: settingsCtr.entity.pccEntries){
    if(!entry.pcc){
        throw Xeption.forAdmin("в настройках DQB отчета не заполнены не заполнен PCC");
    }
    if(!entry.gdsAccount){
        throw Xeption.forAdmin("в настройках DQB отчета не заполнены не заполнена учетная запись GDS");
    }
    if(!es.resolve(entry.gdsAccount)){
        throw Xeption.forAdmin("учетная запись GDS ${entry.gdsAccount} удалена");
    }
}

enum DqbReportItemSource{
    AGENCY{
        public String toString(){
            "МОМ";
        }
    },
    GDS{
        public String toString(){
            "ГДС";
        }
    }
}



class DqbReportItem {
    DqbReportItemSource source;
    String ticketNumber;
    String bookingNumber;
    String passengerName;
    ProductStatus status;
    BigDecimal price;
    BigDecimal commisionAmount;
    BigDecimal commissionPercent;
    TravellerDataProcessor proc = new TravellerDataProcessor();
    boolean isTatStock = false;

    DqbReportItem(ProductIndex idx){
        source = DqbReportItemSource.AGENCY;
        ticketNumber = idx.getTicketNumbers().isEmpty()? "": idx.getTicketNumbers().iterator().next();
        bookingNumber = idx.getBookingNumber();
        passengerName = idx.getTravellerName();
        status = idx.getStatus();
        if (idx.operationBatch == OperationBatch.EXCHANGE) {
            price = idx.getClientFopAddAmount();
        } else {
            price = idx.getTotalSum();
        }
        if(ProfileHelper.isOrganizationCode(idx.getBlankOwner(),
                "BSP") || DictHelper.isBspTicket(ticketNumber)){
           commissionPercent = idx.getBspCommissionRate() ==null? null : BigDecimal.valueOf(idx.getBspCommissionRate().doubleValue());
           commisionAmount = idx.getBspCommissionValue();
        }else{
            commissionPercent = idx.getVendorCommissionRate() ==null? null : BigDecimal.valueOf(idx.getVendorCommissionRate());
            commisionAmount = idx.getVendorCommissionValue();
        }
        cleanupPassengerName();
    }

    DqbReportItem(DailySalesReportItem item){
        source = DqbReportItemSource.GDS;
        ticketNumber = item.getTicketNumber()? item.getTicketNumber().substring(3):item.getTicketNumber();
        passengerName = item.getPassenger();
        status = item.getStatus();
        price = item.getPrice();
        cleanupPassengerName();
        commisionAmount = item.getCommissionAmount();
        commissionPercent = item.getCommissionPercent();
    }

    private void cleanupPassengerName(){
        if(!passengerName){
            return
        }
        MessageContext ctx = new MessageContext();
        ctx.putObject(IBusParsersContextKeys.TRAVELLER_NAME, passengerName);
        proc.process(ctx);
        Traveller trav = ctx.getObject(IBusParsersContextKeys.TRAVELLER);
        passengerName = trav.getName();
        if(passengerName.endsWith(" M")){
            passengerName = passengerName.substring(0, passengerName.length()-2);
        }
    }

}

Map<String, DqbReportItem> momTicketsMap = new HashMap<>()
List<String> pccList = settingsCtr.entity.pccEntries.collect { it.pcc }
tickets{ ProductIndex it ->
    if (pccList.contains(it.pcc)) {
        DqbReportItem item = new DqbReportItem(it)
        String key = (item.ticketNumber ? item.ticketNumber :
                UUIDGenerator.generate().toString()) + (item.status ? item.status.name() : "UNDEFINED")
        momTicketsMap.put(key, item)
    }
}
Map<String, DqbReportItem> gdsTicketsMap = new HashMap<>();
def startDate = MiscUtil.clearTime(parameters['reportDate'])
def endDate = MiscUtil.setDayEndTime(parameters['reportDate'])

int size = settingsCtr.entity.pccEntries.size();
int idx = 0;
settingsCtr.entity.pccEntries.each {pccEntry ->
    callback.updateProgress((byte) (60d+40d*idx/size), 'получаем данные из ' + pccEntry.pcc);
    def dailyReportParameters = new DailySalesReportParameters();
    dailyReportParameters.date = parameters['reportDate']
    dailyReportParameters.pcc = pccEntry.pcc
    dailyReportParameters.trace = parameters['saveGdsTrace']
    dailyReportParameters.includeVoided = true
    dailyReportParameters.includeRefunded = true
    dailyReportParameters.setQualifier(pccEntry.qualifier)
    def data = [:]
    data['GDS'] = GdsName.SABRE
    data['GDS_ACCOUNT'] = es.resolve(pccEntry.gdsAccount)
    data['DAILY_SALES_REPORT_PARAMETERS']= dailyReportParameters
    data['SALES_CONTEXT']= new SalesContext();
    data['SabreDQBVersion'] = pccEntry.isUseNewVersion()? "NEW":"OLD"
    try {
        Environment.getPublished(IntegrationBusFacade.class).getRequestReplyAdapter('gds:sabre:daily-sales-report:daily-sales-report').processSync(data);
    } catch (Exception e){
        throw Xeption.forEndUser("не удалось получить данные по pcc ${pccEntry.pcc}", e)
    }
    def reportData = data["DAILY_SALES_REPORT_ITEMS"];
    info("loaded gds items " + reportData)
    reportData.each {DailySalesReportItem item ->
        String key = (item.ticketNumber? item.ticketNumber.substring(3): UUIDGenerator.generate().toString()) + (item.status? item.status.name():"UNDEFINED");
        gdsTicketsMap[key] = new DqbReportItem(item)
        gdsTicketsMap[key].isTatStock = pccEntry.getQualifier() == "TAT";
    }
}

callback.updateProgress((byte) 99, "сравниваем данные");

List<Pair<DqbReportItem>> items = []
momTicketsMap.entrySet().each { entry ->
    def key = entry.key;
    def momItem = entry.value
    def gdsItem = gdsTicketsMap.get(key);
    if(!gdsItem){
        items.add(new Pair<DqbReportItem>(momItem, null))
        return;
    }
    gdsTicketsMap.remove(key)
    boolean differs = false;
    if (momItem.passengerName != null && gdsItem.passengerName != null) {
        if (!StringUtils.containsIgnoreCase(momItem.passengerName, gdsItem.passengerName) &&
                !StringUtils.containsIgnoreCase(gdsItem.passengerName, momItem.passengerName)) {
            differs = true
        }
    }
    if (momItem.status != ProductStatus.VOID && gdsItem.status != ProductStatus.VOID) {
        if (MiscUtil.compare(
                momItem.price?.abs(),
                gdsItem.price?.abs() + (gdsItem.isTatStock ? gdsItem.commisionAmount?.abs() : 0),
                false) != 0) {
            differs = true
        }
    }
    /*
    if(MiscUtil.compare(momItem.commissionPercent, gdsItem.commissionPercent,false) != 0){
        differs = true;
    }
    if(MiscUtil.compare(momItem.commisionAmount, gdsItem.commisionAmount,false) != 0){
        differs = true;
    }*/
    if(differs){
        items.add(new Pair<DqbReportItem>(momItem, gdsItem))
    }
}
gdsTicketsMap.entrySet().each { entry ->
    items.add(new Pair<DqbReportItem>(null, entry.value))
}
items.sort(new Comparator<Pair<DqbReportItem>>() {
    @Override
    int compare(Pair<DqbReportItem> o1, Pair<DqbReportItem> o2) {
        if(!o1.first){
            return o2.first? 1: 0;
        }
        if(!o2.first){
            return -1;
        }
        if(!o1.last){
            return o2.last? 1: 0
        }
        if(!o2.last){
            return -1;
        }
        return MiscUtil.compare(o1.first.ticketNumber, o2.first.ticketNumber);
    }
});



def reportDate = new java.text.SimpleDateFormat("dd.MM.yyyy").format(parameters['reportDate']);



info("has ${items.size()} items to report")

page{"Сверка"}{
    rowHeight(25)
    text('Отчет/сверка с Sabre (DQB) за ' + reportDate, 'title')
    2.times{nextRow()}

    columnWidth(12)
    text('Источник', 'header')
    nextColumn()
    columnWidth(15)
    text('Номер заказа', 'header')
    nextColumn()
    text('Номер билета', 'header')
    columnWidth(20)
    nextColumn()
    text('Статус', 'header')
    nextColumn()
    columnWidth(20)
    text('Пассажир', 'header')
    nextColumn()
    text('Cтоимость билета', 'header')
//    nextColumn()
//    text('% Комиссии', 'header')
//    nextColumn()
//    text('Сумма комиссии', 'header')
    items.each {pair ->
        def momItem =pair.first
        def gdsItem = pair.last
        boolean equalsByPassengerName = MiscUtil.compare(momItem?.passengerName, gdsItem?.passengerName,false) == 0;
        boolean equalsByPrice = MiscUtil.compare(momItem?.price, gdsItem?.price,false) == 0;
//        boolean equalsByCommissionRate = false && MiscUtil.compare(momItem?.commissionPercent, gdsItem?.commissionPercent,false) == 0;
//        boolean equalsByCommissionAmount = false && MiscUtil.compare(momItem?.commisionAmount, gdsItem?.commisionAmount,false) == 0;
        if(momItem){
            nextRow()
            text(momItem.source.toString(),"textData")
            nextColumn()
            text(momItem.bookingNumber,"textData")
            nextColumn()
            text(momItem.ticketNumber,"textData")
            nextColumn()
            text(momItem.status.toString(),"textData")
            nextColumn()
            text(equalsByPassengerName? " ": momItem.passengerName,"textData")
            nextColumn()
            number(equalsByPrice? null: momItem.price,"textData")
//            nextColumn()
//            number(equalsByCommissionRate? null: momItem.commissionPercent,"textData")
//            nextColumn()
//            number(equalsByCommissionAmount? null: momItem.commisionAmount,"textData")
        }
        if(gdsItem){
            nextRow()
            text(gdsItem.source.toString(),"textData")
            nextColumn()
            text(" ","textData")
            nextColumn()
            text(gdsItem.ticketNumber,"textData")
            nextColumn()
            text(gdsItem.status.toString(),"textData")
            nextColumn()
            text(equalsByPassengerName? " ": gdsItem.passengerName,"textData")
            nextColumn()
            number(equalsByPrice? null: gdsItem.price,"textData")
//            nextColumn()
//            number(equalsByCommissionRate? null: momItem.commissionPercent,"textData")
//            nextColumn()
//            number(equalsByCommissionAmount? null: momItem.commisionAmount,"textData")
        }

    }
}