package com.knecon.fforesight.service.layoutparser.server.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import com.iqser.red.commons.jackson.ObjectMapperFactory;
import com.iqser.red.storage.commons.service.StorageService;
import com.iqser.red.storage.commons.utils.FileSystemBackedStorageService;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingRequest;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingStorageService;
import com.knecon.fforesight.service.layoutparser.server.Application;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantsClient;

import lombok.SneakyThrows;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Import(AbstractTest.TestConfiguration.class)
public abstract class AbstractTest {

    @Autowired
    protected LayoutParsingStorageService layoutParsingStorageService;

    @Autowired
    protected StorageService storageService;

    @MockBean
    protected TenantsClient tenantsClient;

    @MockBean
    private RabbitTemplate rabbitTemplate;

    protected final static String ORIGIN_FILE_ID = "origin";

    protected final static String VISUAL_LAYOUT_FILE = "visual";
    protected final static String TABLE_FILE_ID = "table";
    protected final static String IMAGE_FILE_ID = "image";
    protected final static String STRUCTURE_FILE_ID = "structure";
    protected final static String TEXT_FILE_ID = "texts";
    protected final static String POSITION_FILE_ID = "positions";
    protected final static String PAGES_FILE_ID = "pages";
    protected final static String TENANT_ID = "tenant";
    protected final static String VIEWER_DOCUMENT_ID = "viewer";
    protected final static String SIMPLIFIED_ID = "simplified";


    protected LayoutParsingRequest buildStandardLayoutParsingRequest() {

        return LayoutParsingRequest.builder()
                .layoutParsingType(LayoutParsingType.REDACT_MANAGER_OLD)
                .originFileStorageId(ORIGIN_FILE_ID)
                .tablesFileStorageId(Optional.of(TABLE_FILE_ID))
                .imagesFileStorageId(Optional.of(IMAGE_FILE_ID))
                .structureFileStorageId(STRUCTURE_FILE_ID)
                .textBlockFileStorageId(TEXT_FILE_ID)
                .positionBlockFileStorageId(POSITION_FILE_ID)
                .pageFileStorageId(PAGES_FILE_ID)
                .simplifiedTextStorageId(SIMPLIFIED_ID)
                .viewerDocumentStorageId(VIEWER_DOCUMENT_ID)
                .build();
    }


    @BeforeEach
    public void setupTenantContext() {

        TenantContext.setTenantId(TENANT_ID);
    }


    @AfterEach
    public void clearTenantContext() {

        TenantContext.clear();
    }


    @SneakyThrows
    protected LayoutParsingRequest prepareStorage(String file) {

        return prepareStorage(file, "cv_table_parsing_response/empty.json", "image_service_response/empty.json", "visual_layout_parsing_response/empty.json");
    }


    protected LayoutParsingRequest buildDefaultLayoutParsingRequest(String fileName, LayoutParsingType layoutParsingType, boolean debug) {

        var identifier = debug ? Map.of("fileId", fileName, "debug", "true") : Map.of("fileId", fileName);
        return LayoutParsingRequest.builder()
                .identifier(identifier)
                .layoutParsingType(layoutParsingType)
                .originFileStorageId(fileName + ORIGIN_FILE_ID)
                .tablesFileStorageId(Optional.of(fileName + TABLE_FILE_ID))
                .imagesFileStorageId(Optional.of(fileName + IMAGE_FILE_ID))
                .visualLayoutParsingFileId(Optional.empty())
                .structureFileStorageId(fileName + STRUCTURE_FILE_ID)
                .textBlockFileStorageId(fileName + TEXT_FILE_ID)
                .positionBlockFileStorageId(fileName + POSITION_FILE_ID)
                .pageFileStorageId(fileName + PAGES_FILE_ID)
                .simplifiedTextStorageId(fileName + SIMPLIFIED_ID)
                .viewerDocumentStorageId(fileName + VIEWER_DOCUMENT_ID)
                .build();
    }


    @SneakyThrows
    protected LayoutParsingRequest prepareStorage(String file, String cvServiceResponseFile, String imageInfoFile) {

        ClassPathResource pdfFileResource = new ClassPathResource(file);
        ClassPathResource cvServiceResponseFileResource = new ClassPathResource(cvServiceResponseFile);
        ClassPathResource imageInfoFileResource = new ClassPathResource(imageInfoFile);

        return prepareStorage(pdfFileResource.getInputStream(), cvServiceResponseFileResource.getInputStream(), imageInfoFileResource.getInputStream());
    }


    @SneakyThrows
    protected LayoutParsingRequest prepareStorage(String file, String cvServiceResponseFile, String imageInfoFile, String visualLayoutParsingResponseFile) {

        ClassPathResource pdfFileResource = new ClassPathResource(file);
        ClassPathResource cvServiceResponseFileResource = new ClassPathResource(cvServiceResponseFile);
        ClassPathResource imageInfoFileResource = new ClassPathResource(imageInfoFile);
        ClassPathResource visualLayoutParsingResponseResource = new ClassPathResource(visualLayoutParsingResponseFile);

        return prepareStorage(Path.of(file).getFileName().toString(),
                              pdfFileResource.getInputStream(),
                              cvServiceResponseFileResource.getInputStream(),
                              imageInfoFileResource.getInputStream(),
                              visualLayoutParsingResponseResource.getInputStream());
    }


    @SneakyThrows
    protected void prepareStorage(LayoutParsingRequest layoutParsingRequest, File file) {

        ClassPathResource cvServiceResponseFileResource = new ClassPathResource("cv_table_parsing_response/empty.json");
        ClassPathResource imageInfoFileResource = new ClassPathResource("image_service_response/empty.json");
        ClassPathResource visualLayoutParsingResponseResource = new ClassPathResource("visual_layout_parsing_response/empty.json");

        try (var in = new FileInputStream(file)) {
            prepareStorage(layoutParsingRequest,
                           in,
                           cvServiceResponseFileResource.getInputStream(),
                           imageInfoFileResource.getInputStream(),
                           visualLayoutParsingResponseResource.getInputStream());
        }
    }


    @SneakyThrows
    protected LayoutParsingRequest prepareStorage(InputStream fileStream, InputStream cvServiceResponseFileStream, InputStream imageInfoStream) {

        storageService.storeObject(TenantContext.getTenantId(), IMAGE_FILE_ID, imageInfoStream);
        storageService.storeObject(TenantContext.getTenantId(), TABLE_FILE_ID, cvServiceResponseFileStream);
        storageService.storeObject(TenantContext.getTenantId(), ORIGIN_FILE_ID, fileStream);

        return buildDefaultLayoutParsingRequest("test", LayoutParsingType.REDACT_MANAGER_OLD, true);
    }


    @SneakyThrows
    protected void prepareStorage(LayoutParsingRequest layoutParsingRequest,
                                  InputStream fileStream,
                                  InputStream cvServiceResponseFileStream,
                                  InputStream imageInfoStream,
                                  InputStream visualLayoutParsingResponseFileStream) {

        storageService.storeObject(TenantContext.getTenantId(), layoutParsingRequest.imagesFileStorageId().get(), imageInfoStream);
        storageService.storeObject(TenantContext.getTenantId(), layoutParsingRequest.tablesFileStorageId().get(), cvServiceResponseFileStream);
        storageService.storeObject(TenantContext.getTenantId(), layoutParsingRequest.originFileStorageId(), fileStream);
        if (layoutParsingRequest.visualLayoutParsingFileId().isPresent()) {
            storageService.storeObject(TenantContext.getTenantId(), layoutParsingRequest.visualLayoutParsingFileId().get(), visualLayoutParsingResponseFileStream);
        }
    }


    @SneakyThrows
    protected LayoutParsingRequest prepareStorage(String fileName,
                                                  InputStream fileStream,
                                                  InputStream cvServiceResponseFileStream,
                                                  InputStream imageInfoStream,
                                                  InputStream visualLayoutParsingResponseFileStream) {

        storageService.storeObject(TenantContext.getTenantId(), IMAGE_FILE_ID, imageInfoStream);
        storageService.storeObject(TenantContext.getTenantId(), TABLE_FILE_ID, cvServiceResponseFileStream);
        storageService.storeObject(TenantContext.getTenantId(), ORIGIN_FILE_ID, fileStream);
        storageService.storeObject(TenantContext.getTenantId(), VISUAL_LAYOUT_FILE, visualLayoutParsingResponseFileStream);

        return buildDefaultLayoutParsingRequest(fileName, LayoutParsingType.REDACT_MANAGER_OLD, true);
    }


    @AfterEach
    public void cleanupStorage() {

        if (this.storageService instanceof FileSystemBackedStorageService) {
            ((FileSystemBackedStorageService) this.storageService).clearStorage();
        }
    }


    @Configuration
    @EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class)
    @ComponentScan("com.knecon.fforesight.service.layoutparser")
    public static class TestConfiguration {

        @Bean
        @Primary
        public StorageService inmemoryStorage() {

            return new FileSystemBackedStorageService(ObjectMapperFactory.create());
        }

    }

}
