package ru.nbch.credit_tracker.utils;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.enums.SignalError;
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
import ru.nbch.credit_tracker.exceptions.SignalClientException;
import ru.nbch.credit_tracker.service.xml.stax.MonitoringRequestStaxReader;
import ru.nbch.credit_tracker.service.xml.stax.StaxReader;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.service.xml.stax.XmlProcessorStaxImpl;

import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.zip.ZipInputStream;

import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;

@SpringBootTest(classes = {ValidationUtilTest.TestConfig.class})
public class ValidationUtilTest {

    private final StaxXmlProcessor staxXmlProcessor;

    @Autowired
    public ValidationUtilTest(StaxXmlProcessor staxXmlProcessor) {
        this.staxXmlProcessor = staxXmlProcessor;
    }

    @Configuration
    public static class TestConfig {
        @Bean
        public StaxXmlProcessor xmlProcessor() {
            List<Supplier<StaxReader<?>>> staxReader = new ArrayList<>();
            staxReader.add(MonitoringRequestStaxReader::new);
            return new XmlProcessorStaxImpl(staxReader);
        }
    }

    private static Stream<Arguments> validationTestCases() {
        return Stream.of(
                Arguments.of(
                        "/monitoring_request/valid_request.zip",
                        null,
                        null
                ),
                Arguments.of(
                        "/monitoring_request/invalid_xml.zip",
                        SignalError.ERROR_011,
                        Collections.emptyList().toArray(new Object[0])
                )
                ,
                Arguments.of(
                        "/monitoring_request/no_required_tag_request.zip",
                        SignalError.ERROR_002,
                        new Object[]{"Password"}
                ),
                Arguments.of(
                        "/monitoring_request/invalid_phone_request.zip",
                        SignalError.ERROR_003,
                        new Object[]{"abc123"}
                ),
                Arguments.of(
                        "/monitoring_request/invalid_id_request.zip",
                        SignalError.ERROR_010,
                        new Object[]{"#####"}
                )
        );
    }

    @ParameterizedTest(name = "[{index}] PATH: [ {0} ] SIGNALERROR: [ {1} ] ARGS [ {2} ]")
    @MethodSource("validationTestCases")
    void testMonitoringRequestValidation(String path, SignalError signalError, Object[] args) {
        ZipInputStream zipInputStream = null;
        MonitoringRequest monitoringRequest = null;
        SignalClientException ex = null;
        try {
            zipInputStream = new ZipInputStream(getClass().getResourceAsStream(path));
            zipInputStream.getNextEntry();
            monitoringRequest = staxXmlProcessor.read(zipInputStream, MonitoringRequest.class);
        } catch (Exception e) {
            if (e.getCause() instanceof XMLStreamException) {
                ex = clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError);
            } else {
                throw new RuntimeException(e);
            }
        } finally {
            if (zipInputStream != null) {
                try {
                    zipInputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException();
                }
            }
        }

        if(ex == null) {
            try {
                ValidationUtil.validateMonitoringRequest(monitoringRequest);
            } catch (SignalClientException e) {
                ex = e;
            }
        }
        if (ex != null) {
            Assertions.assertEquals(signalError, ex.getErrorMessage().getSubject());
            Assertions.assertArrayEquals(args, ex.getErrorMessage().getArgs());
        }
    }
}
