import java.nio.file.Files
import java.text.SimpleDateFormat
import java.util.Map.Entry
import java.util.stream.Collectors

import org.apache.poi.ss.usermodel.Row
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.slf4j.Logger
import org.slf4j.LoggerFactory

import com.gridnine.xtrip.common.Environment
import com.gridnine.xtrip.common.l10n.model.LocaleManager
import com.gridnine.xtrip.common.model.EntityContainer
import com.gridnine.xtrip.common.model.EntityReference
import com.gridnine.xtrip.common.model.booking.BaseProduct
import com.gridnine.xtrip.common.model.booking.BookingFile
import com.gridnine.xtrip.common.model.booking.CommonProductIndex
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.TicketType
import com.gridnine.xtrip.common.model.booking.ValidationMessage
import com.gridnine.xtrip.common.model.booking.ValidationMessageCategory
import com.gridnine.xtrip.common.model.booking.ValidationMessageSeverity
import com.gridnine.xtrip.common.model.booking.commission.ProductType
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.handlers.HandlersRegistry
import com.gridnine.xtrip.common.model.handlers.ProductHandler
import com.gridnine.xtrip.common.model.helpers.BookingHelper
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.model.validation.ValidationMessagesResourcesEnvironment
import com.gridnine.xtrip.common.search.SearchCriterion
import com.gridnine.xtrip.common.search.SearchQuery
import com.gridnine.xtrip.common.search.SortOrder
import com.gridnine.xtrip.common.util.MiscUtil.Pair

Logger logger = LoggerFactory.getLogger('groovy-script')

logger.info('Validation products: validation started')

Date startDate = new SimpleDateFormat('yyyy.MM.dd HH:mm:ss.SSS').parse('2019.04.11 00:00:00.000')
Date endDate = new SimpleDateFormat('yyyy.MM.dd HH:mm:ss.SSS').parse('2019.04.20 23:59:59.999')

List<ValidationMessageSeverity> severities = Arrays.asList(
        //ValidationMessageSeverity.MESSAGE,
        //ValidationMessageSeverity.WARNING,
        ValidationMessageSeverity.ERROR,
        ValidationMessageSeverity.USER_ERROR,
        ValidationMessageSeverity.SOURCE_DATA_ERROR
        )

List<ValidationMessageCategory> categories = Arrays.asList(
        ValidationMessageCategory.OTHER,
        //ValidationMessageCategory.STOCK_CONTROL,
        ValidationMessageCategory.RULES
        //ValidationMessageCategory.VAT_CALCULATION,
        //ValidationMessageCategory.TRAVELDOC,
        //ValidationMessageCategory.STATISTICS,
        //ValidationMessageCategory.INTEGRATION_1C,
        //ValidationMessageCategory.PASSIVE_SEGMENTS
        )

logger.info('Validation products: startDate is ' + startDate)
logger.info('Validation products: endDate is ' + endDate)

int countt = 0
int amount = 10000

int processed = 0
int found = 0

Map<String, Pair<String, Integer>> statistics = new HashMap()

Workbook book = new XSSFWorkbook()

try {

    Sheet statisticsSheet = book.createSheet('Statistics')
    Sheet validationSheet = book.createSheet('Validation')

    int rowIndex = 0

    Row headerRow = validationSheet.createRow(rowIndex)

    headerRow.createCell(0).setCellValue('Номер')
    headerRow.createCell(1).setCellValue('Статус')
    headerRow.createCell(2).setCellValue('Тип')
    headerRow.createCell(3).setCellValue('Владелец бланков')
    headerRow.createCell(4).setCellValue('Код')
    headerRow.createCell(5).setCellValue('Сообщение')

    rowIndex++

    while(true) {

        int limit = amount
        int offset = countt * amount

        logger.info('Validation products: searching ' + limit + ' starting from ' + offset)

        SearchQuery query = new SearchQuery()

        query.getCriteria().getCriterions().add(SearchCriterion.ge(CommonProductIndex.Property.issueDate.name(), startDate))
        query.getCriteria().getCriterions().add(SearchCriterion.le(CommonProductIndex.Property.issueDate.name(), endDate))

        query.getCriteria().getCriterions().add(SearchCriterion.ne(CommonProductIndex.Property.ticketType.name(), TicketType.FAKE))
        query.getCriteria().getCriterions().add(SearchCriterion.eq(CommonProductIndex.Property.hasErrors.name(), Boolean.TRUE))

        query.setLimit(limit)
        query.setOffset(offset)

        query.getCriteria().orders.put('containerUid', SortOrder.ASC)

        query.getPreferredProperties().add('containerUid')

        List<CommonProductIndex> indexes = EntityStorage.get().search(CommonProductIndex.class, query).getData()

        logger.info('Validation products: found ' + indexes.size() + ' products')

        if(indexes.size() == 0) {
            break
        }

        for(CommonProductIndex index : indexes) {

            logger.info('Validation products: processing ' + processed + ' | ' + found + ' | ' + index.getSource().getUid())

            EntityContainer<BookingFile> bookingFileContainer = EntityStorage.get().resolve(index.getSource())

            if(bookingFileContainer != null) {

                BaseProduct product =  BookingHelper.findProduct(bookingFileContainer.getEntity(), index.getNavigationKey())

                if(product != null) {

                    ProductHandler<BaseProduct> handler = HandlersRegistry.get().findProductHandler(product.getClass())

                    if(handler != null) {

                        List<String> systemNumbers = handler.getProductNumbers(product)

                        String systemNumber = systemNumbers.size() > 0 ? systemNumbers.get(0) : null
                        ProductStatus productStatus = handler.getStatus(product)
                        ProductType productType = handler.getProductType(product)
                        EntityReference<Organization> blankOwner = handler.getBlankOwner(product)

                        List<ValidationMessage> validationMessages = handler.getValidationMessages(product).stream().filter({ ValidationMessage item -> severities.contains(item.getSeverity()) && categories.contains(item.getCategory())}).collect(Collectors.toList())

                        for(int i = 0; i < validationMessages.size(); i++) {

                            ValidationMessage validationMessage = validationMessages.get(i)

                            String errorCode = validationMessage.getErrorCode()
                            
                            String message = LocaleManager.get().getL10nResourceManager().getMessage(validationMessage.getMessage())

                            Row row = validationSheet.createRow(rowIndex)

                            row.createCell(0).setCellValue(i == 0 ? systemNumber : null)
                            row.createCell(1).setCellValue(i == 0 ? (productStatus != null ? productStatus.toString() : '?') : null)
                            row.createCell(2).setCellValue(i == 0 ? (productType != null ? productType.toString() : '?') : null)
                            row.createCell(3).setCellValue(i == 0 ? (blankOwner != null ? blankOwner.toString() : '?') : null)
                            row.createCell(4).setCellValue(errorCode != null ? errorCode : '?')
                            row.createCell(5).setCellValue(message)

                            statistics.put(errorCode, statistics.get(errorCode) != null ? new Pair<>(statistics.get(errorCode).getFirst(), Integer.valueOf(statistics.get(errorCode).getSecond().intValue() + 1)) : new Pair<>(message, Integer.valueOf(1)))

                            rowIndex++
                        }

                        found++
                    }
                }

            } else {
                logger.info('Validation products: unable to load ' + index.getSource() + ' (' + index.getSource().getUid() + ')')
            }

            processed++
        }

        countt++
    }

    List<Entry<String, Pair<String, Integer>>> entries = new ArrayList<>(statistics.entrySet())
    
    Collections.sort(entries, new Comparator<Entry<String, Pair<String, Integer>>>() {
        
        public int compare(Entry<String, Pair<String, Integer>> o1, Entry<String, Pair<String, Integer>> o2) {
            return o2.getValue().getSecond().compareTo(o1.getValue().getSecond())    
        }
    })
    
    rowIndex = 0

    headerRow = statisticsSheet.createRow(rowIndex)

    headerRow.createCell(0).setCellValue('Тип')
    headerRow.createCell(1).setCellValue('Сообщение')
    headerRow.createCell(2).setCellValue('Количество')

    rowIndex++

    for(Entry<String, Pair<String, Integer>> entry : entries) {

        Row row = statisticsSheet.createRow(rowIndex)

        row.createCell(0).setCellValue(entry.getKey())
        row.createCell(1).setCellValue(entry.getValue().getFirst())
        row.createCell(2).setCellValue(entry.getValue().getSecond().doubleValue())

        rowIndex++
    }

    OutputStream os = Files.newOutputStream(Files.createDirectories(Environment.getDataFolder().toPath().resolve('export/validation')).resolve('validation-products.xlsx'))

    try {
        book.write(os)
    } finally {
        os.close()
    }

} catch(Throwable t) {
    logger.error('Validation products: error occured', t)
} finally {
    book.close()
}

logger.info('Validation products: processed ' + processed)
logger.info('Validation products: found ' + found)

logger.info('Validation products: validation finished')
