package inji.api.testscripts;

import inji.utils.InjiWalletConfigManager;
import inji.utils.InjiWalletUtil;
import io.mosip.testrig.apirig.dto.OutputValidationDto;
import io.mosip.testrig.apirig.dto.TestCaseDTO;
import io.mosip.testrig.apirig.testrunner.BaseTestCase;
import io.mosip.testrig.apirig.testrunner.HealthChecker;
import io.mosip.testrig.apirig.utils.*;
import io.restassured.response.Response;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.json.JSONObject;
import org.testng.*;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.internal.BaseTestMethod;
import org.testng.internal.TestResult;

import java.lang.reflect.Field;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SimplePostForAutoGenId extends InjiWalletUtil implements ITest {
    private static final Logger logger = Logger.getLogger(SimplePostForAutoGenId.class);
    protected String testCaseName = "";
    public String idKeyName = null;
    public Response response = null;
    public boolean sendEsignetToken = false;
    public boolean auditLogCheck = false;

    @BeforeClass
    public static void setLogLevel() {
        if (InjiWalletConfigManager.IsDebugEnabled())
            logger.setLevel(Level.ALL);
        else
            logger.setLevel(Level.ERROR);
    }

    /**
     * get current testcaseName
     */
    @Override
    public String getTestName() {
        return testCaseName;
    }

    /**
     * Data provider class provides test case list
     *
     * @return object of data provider
     */
    @DataProvider(name = "testcaselist")
    public Object[] getTestCaseList(ITestContext context) {
        String ymlFile = context.getCurrentXmlTest().getLocalParameters().get("ymlFile");
        sendEsignetToken = context.getCurrentXmlTest().getLocalParameters().containsKey("sendEsignetToken");
        idKeyName = context.getCurrentXmlTest().getLocalParameters().get("idKeyName");
        logger.info("Started executing yml: " + ymlFile);
        return getYmlTestData(ymlFile);
    }

    /**
     * Test method for OTP Generation execution
     *
     * @param testCaseDTO
     * @throws AuthenticationTestException
     * @throws AdminTestException
     * @throws NoSuchAlgorithmException
     * @throws SecurityXSSException
     */
    @Test(dataProvider = "testcaselist")
    public void test(TestCaseDTO testCaseDTO)
            throws AuthenticationTestException, AdminTestException, NoSuchAlgorithmException, SecurityXSSException {
        testCaseName = testCaseDTO.getTestCaseName();
        if (HealthChecker.signalTerminateExecution) {
            throw new SkipException(
                    GlobalConstants.TARGET_ENV_HEALTH_CHECK_FAILED + HealthChecker.healthCheckFailureMapS);
        }
        if (testCaseDTO.getTestCaseName().contains("VID") || testCaseDTO.getTestCaseName().contains("Vid")) {
            if (!BaseTestCase.getSupportedIdTypesValueFromActuator().contains("VID")
                    && !BaseTestCase.getSupportedIdTypesValueFromActuator().contains("vid")) {
                throw new SkipException(GlobalConstants.VID_FEATURE_NOT_SUPPORTED);
            }
        }

        testCaseDTO = InjiWalletUtil.isTestCaseValidForTheExecution(testCaseDTO);
        String[] templateFields = testCaseDTO.getTemplateFields();
        String inputJson = "";

        inputJson = getJsonFromTemplate(testCaseDTO.getInput(), testCaseDTO.getInputTemplate());
        inputJson = InjiWalletUtil.inputstringKeyWordHandeler(inputJson, testCaseName);

        String outputJson = getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate());

        if (testCaseDTO.getTemplateFields() != null && templateFields.length > 0) {
            ArrayList<JSONObject> inputtestCases = AdminTestUtil.getInputTestCase(testCaseDTO);
            ArrayList<JSONObject> outputtestcase = AdminTestUtil.getOutputTestCase(testCaseDTO);
            for (int i = 0; i < languageList.size(); i++) {
                response = postWithBodyAndCookieForAutoGeneratedId(ApplnURI + testCaseDTO.getEndPoint(),
                        getJsonFromTemplate(inputtestCases.get(i).toString(), testCaseDTO.getInputTemplate()),
                        COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), idKeyName);

                Map<String, List<OutputValidationDto>> ouputValid = OutputValidationUtil.doJsonOutputValidation(
                        response.asString(),
                        getJsonFromTemplate(outputtestcase.get(i).toString(), testCaseDTO.getOutputTemplate()),
                        testCaseDTO, response.getStatusCode());
                if (testCaseDTO.getTestCaseName().toLowerCase().contains("dynamic")) {
                    JSONObject json = new JSONObject(response.asString());
                    idField = json.getJSONObject("response").get("id").toString();
                }
                Reporter.log(ReportUtil.getOutputValidationReport(ouputValid));

                if (!OutputValidationUtil.publishOutputResult(ouputValid))
                    throw new AdminTestException("Failed at output validation");
            }
        } else {
            if (testCaseDTO.getEndPoint().startsWith("$SUNBIRDBASEURL$") && testCaseName.contains("SunBirdR")) {
                String tempUrl = ApplnURI;
                if (InjiWalletConfigManager.isInServiceNotDeployedList("sunbirdrc"))
                    throw new SkipException(GlobalConstants.SERVICE_NOT_DEPLOYED_MESSAGE);

                if (InjiWalletConfigManager.getSunbirdBaseURL() != null && !InjiWalletConfigManager.getSunbirdBaseURL().isBlank())
                    tempUrl = InjiWalletConfigManager.getSunbirdBaseURL();
                testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$SUNBIRDBASEURL$", ""));


                if (testCaseName.contains("SunBirdR_CreatePolicy_")) {
                    //Adding the while loop for try creating sunbird policy in registry for 3 times if gets failed
                    int currLoopCount = 0;
                    do {
                        response = postWithBodyAndBearerTokenForAutoGeneratedId(tempUrl + testCaseDTO.getEndPoint(),
                                inputJson, COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), idKeyName);

                        if (response != null && !response.asString().contains("UNSUCCESSFUL")) {
                            break;
                        }
                        currLoopCount++;
                    } while (currLoopCount < 10);
                } else {
                    response = postWithBodyAndBearerTokenForAutoGeneratedId(tempUrl + testCaseDTO.getEndPoint(),
                            inputJson, COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), idKeyName);
                }
            } else {
                response = postWithBodyAndCookieForAutoGeneratedId(ApplnURI + testCaseDTO.getEndPoint(), inputJson,
                        auditLogCheck, COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), idKeyName,
                        sendEsignetToken);
            }

            Map<String, List<OutputValidationDto>> ouputValid = null;
            if (testCaseName.contains("_StatusCode")) {

                OutputValidationDto customResponse = customStatusCodeResponse(String.valueOf(response.getStatusCode()),
                        testCaseDTO.getOutput());

                ouputValid = new HashMap<>();
                ouputValid.put(GlobalConstants.EXPECTED_VS_ACTUAL, List.of(customResponse));
            } else {
                ouputValid = OutputValidationUtil.doJsonOutputValidation(response.asString(),
                        getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate()), testCaseDTO,
                        response.getStatusCode());
            }
            Reporter.log(ReportUtil.getOutputValidationReport(ouputValid));
            if (!OutputValidationUtil.publishOutputResult(ouputValid))
                throw new AdminTestException("Failed at output validation");
        }

    }

    /**
     * The method ser current test name to result
     *
     * @param result
     */
    @AfterMethod(alwaysRun = true)
    public void setResultTestName(ITestResult result) {
        try {
            Field method = TestResult.class.getDeclaredField("m_method");
            method.setAccessible(true);
            method.set(result, result.getMethod().clone());
            BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod();
            Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName");
            f.setAccessible(true);
            f.set(baseTestMethod, testCaseName);
        } catch (Exception e) {
            Reporter.log("Exception : " + e.getMessage());
        }
    }
}