/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

package org.apache.hertzbeat.manager.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.manager.pojo.dto.Hierarchy;
import org.apache.hertzbeat.manager.pojo.dto.MonitorDefineDto;
import org.apache.hertzbeat.manager.pojo.dto.ParamDefineInfo;
import org.apache.hertzbeat.manager.service.impl.AppServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

/**
 * Test case for {@link AppController}
 */
@ExtendWith(MockitoExtension.class)
class AppControllerTest {

    private MockMvc mockMvc;

    @Mock
    private AppServiceImpl appService;

    @InjectMocks
    private AppController appController;

    @BeforeEach
    void setUp() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(appController).build();
    }

    @Test
    void queryAppParamDefines() throws Exception {
        // Data to make
        List<ParamDefineInfo> paramDefines = new ArrayList<>();
        ParamDefineInfo paramDefine = new ParamDefineInfo();
        paramDefine.setId(1L);
        paramDefine.setApp("tanCloud");
        paramDefine.setName(new HashMap<>());
        paramDefine.setField("port");
        paramDefine.setType("number");
        paramDefine.setDefaultValue("12");
        paramDefine.setPlaceholder("请输出密码");
        paramDefine.setCreator("tom");
        paramDefine.setModifier("tom");
        paramDefines.add(paramDefine);

        // The interface is called to return manufactured data
        Mockito.when(appService.getAppParamDefines("api"))
                .thenReturn(paramDefines);

        // Request interface
        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/apps/{app}/params", "api"))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andExpect(jsonPath("$.data[0].id").value(1))
                .andReturn();
    }

    @Test
    void queryPushDefine() throws Exception {
        // Data to make
        Job mockJob = new Job();
        mockJob.setId(1L);
        mockJob.setMonitorId(1L);
        mockJob.setCategory("os");
        mockJob.setApp("mac");
        mockJob.setName(new HashMap<>());
        mockJob.setMetrics(new ArrayList<>());
        mockJob.setConfigmap(new ArrayList<>());


        // The interface is called to return manufactured data
        Mockito.when(appService.getPushDefine(1L)).thenReturn(mockJob);

        // Request interface
        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/apps/{monitorId}/pushdefine", 1L))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andExpect(jsonPath("$.data.id").value(1))
                .andExpect(jsonPath("$.data.app").value("mac"))
                .andReturn();
    }

    @Test
    void queryAutoGenerateDynamicAppDefine() throws Exception {
        // Data to make
        Job mockJob = new Job();
        mockJob.setId(1L);
        mockJob.setMonitorId(1L);
        mockJob.setCategory("os");
        mockJob.setApp("huawei");
        mockJob.setName(new HashMap<>());
        mockJob.setMetrics(new ArrayList<>());
        mockJob.setConfigmap(new ArrayList<>());

        // The interface is called to return manufactured data
        Mockito.when(appService.getAutoGenerateDynamicDefine(1L)).thenReturn(mockJob);

        // Request interface
        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/apps/{monitorId}/define/dynamic", 1L))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andExpect(jsonPath("$.data.id").value(1))
                .andExpect(jsonPath("$.data.app").value("huawei"))
                .andReturn();
    }

    @Test
    void queryAppDefineYml() throws Exception {
        // Data to make
        String defineContent = "defineContent";

        // The interface is called to return manufactured data
        Mockito.when(appService.getMonitorDefineFileContent("api"))
                .thenReturn(defineContent);

        // Request interface
        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/apps/{app}/define/yml", "api"))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andExpect(jsonPath("$.data").value(defineContent))
                .andReturn();
    }

    @Test
    void deleteAppDefineYml_Success() throws Exception {
        // Mock the service call
        Mockito.doNothing().when(appService).deleteMonitorDefine("api");

        // Perform the request and verify the response
        this.mockMvc.perform(MockMvcRequestBuilders.delete("/api/apps/{app}/define/yml", "api"))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andReturn();

        // Verify that the service method was called
        Mockito.verify(appService, Mockito.times(1)).deleteMonitorDefine("api");
    }

    @Test
    void deleteAppDefineYml_Failure() throws Exception {
        // Mock the service call to throw an exception
        Mockito.doThrow(new RuntimeException("Delete failed")).when(appService).deleteMonitorDefine("api");

        // Perform the request and verify the response
        this.mockMvc.perform(MockMvcRequestBuilders.delete("/api/apps/{app}/define/yml", "api"))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.FAIL_CODE))
                .andExpect(jsonPath("$.msg").value("Delete failed"))
                .andReturn();

        // Verify that the service method was called
        Mockito.verify(appService, Mockito.times(1)).deleteMonitorDefine("api");
    }

    @Test
    void newAppDefineYml() throws Exception {
        // Data to make
        MonitorDefineDto defineDto = new MonitorDefineDto();
        defineDto.setDefine("valid-define");
        String defineValue = "test-define";

        String jsonString = "{\"define\":\"" + defineValue.replace("\"", "\\\"") + "\"}";

        // The interface is called to return manufactured data
        Mockito.doNothing().when(appService).applyMonitorDefineYml(Mockito.anyString(), Mockito.anyBoolean());

        // Request interface
        this.mockMvc.perform(post("/api/apps/define/yml")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(jsonString))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andReturn();
    }

    @Test
    void queryAppDefine() throws Exception {
        // Data to make
        Job define = new Job();
        define.setId(1L);
        define.setMonitorId(1L);
        define.setCategory("os");
        define.setApp("linux");
        define.setName(new HashMap<>());
        define.setMetrics(new ArrayList<>());
        define.setConfigmap(new ArrayList<>());

        // The interface is called to return manufactured data
        Mockito.when(appService.getAppDefine("api"))
                .thenReturn(define);

        // Request interface
        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/apps/{app}/define", "api"))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andExpect(jsonPath("$.data.id").value(1))
                .andReturn();
    }

    @Test
    void queryAppsHierarchy() throws Exception {
        // Data to make
        Hierarchy hierarchy = new Hierarchy();
        hierarchy.setLabel("Linux系统");
        hierarchy.setValue("linux");
        hierarchy.setCategory("os");
        List<Hierarchy> list = new ArrayList<>();
        list.add(hierarchy);

        // The interface is called to return manufactured data
        Mockito.when(appService.getAllAppHierarchy("zh-CN"))
                .thenReturn(list);

        // Request interface
        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/apps/hierarchy", "zh-CN"))
                .andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
                .andExpect(jsonPath("$.data[0].category").value("os"))
                .andReturn();
    }
}
