package com.walmartlabs.concord.it.runtime.v2;

/*-
 * *****
 * Concord
 * -----
 * Copyright (C) 2017 - 2023 Walmart Inc.
 * -----
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =====
 */

import ca.ibodrov.concord.testcontainers.ConcordProcess;
import ca.ibodrov.concord.testcontainers.Payload;
import ca.ibodrov.concord.testcontainers.Processes;
import ca.ibodrov.concord.testcontainers.junit5.ConcordRule;
import com.walmartlabs.concord.ApiClient;
import com.walmartlabs.concord.ApiException;
import com.walmartlabs.concord.client.*;
import com.walmartlabs.concord.common.IOUtils;
import com.walmartlabs.concord.sdk.Constants;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.UUID;

import static com.walmartlabs.concord.it.common.ITUtils.randomString;
import static com.walmartlabs.concord.it.runtime.v2.ConcordConfiguration.getServerUrlForAgent;
import static org.junit.jupiter.api.Assertions.*;

public class SessionStateFilesIT extends AbstractTest {

    @RegisterExtension
    public static final ConcordRule concord = ConcordConfiguration.configure();

    @Test
    public void testSessionFileAccess() throws Exception {
        String username = "user_" + randomString();

        UsersApi usersApi = new UsersApi(concord.apiClient());
        CreateUserResponse user = usersApi.createOrUpdate(new CreateUserRequest()
                .setUsername(username)
                .setType(CreateUserRequest.TypeEnum.LOCAL));

        ApiKeysApi apiKeysApi = new ApiKeysApi(concord.apiClient());
        CreateApiKeyResponse cakr = apiKeysApi.create(new CreateApiKeyRequest()
                        .setUserId(user.getId()));

        // ---
        Payload payload = new Payload()
                .archive(resource("sessionFileAccess"))
                .arg("baseUrl", getServerUrlForAgent(concord));

        ApiClient client = concord.apiClient().setApiKey(cakr.getKey());
        ConcordProcess proc = new Processes(client)
                .start(payload);

        // ---
        expectStatus(proc, ProcessEntry.StatusEnum.SUSPENDED);

        // ---
        Assertions.assertEquals(username, proc.getEntry().getInitiator());

        ProcessApi processApiAdmin = new ProcessApi(concord.apiClient());
        ProcessApi processApiInitiator = new ProcessApi(client);

        String file = "sensitive_data.txt";

        // ---
        assertCantDownloadFile(processApiAdmin, proc.instanceId(), file);

        // ---
        assertCantDownloadFile(processApiInitiator, proc.instanceId(), file);

        FormSubmitResponse fsr = proc.submitForm("myForm", Collections.emptyMap());
        assertNull(fsr.getErrors());
        expectStatus(proc, ProcessEntry.StatusEnum.FINISHED);

        // ---
        proc.assertLog(".*Secret: top-secret-value.*");
    }

    private static void assertCantDownloadFile(ProcessApi processApi, UUID instanceId, String file) throws Exception {
        try {
            processApi.downloadAttachment(instanceId, Constants.Files.JOB_SESSION_FILES_DIR_NAME + "/" + file);
            fail("exception expected");
        } catch (ApiException e) {
            assertEquals(403, e.getCode());
        }

        String fullFileName = Constants.Files.JOB_ATTACHMENTS_DIR_NAME + "/" + Constants.Files.JOB_SESSION_FILES_DIR_NAME + "/" + file;

        // ---
        File state = processApi.downloadState(instanceId);
        assertNoFileInState(fullFileName, state);

        // ---
        try {
            processApi.downloadStateFile(instanceId, fullFileName);
            fail("exception expected");
        } catch (ApiException e) {
            assertEquals(403, e.getCode());
        }
    }

    private static void assertNoFileInState(String file, File state) throws IOException  {
        Path target = Files.createTempDirectory("state-unzip");
        IOUtils.unzip(state.toPath(), target, false, (sourceFile, dstFile) -> {
            assertNotEquals(file, target.relativize(dstFile).toString());
        });
    }
}
