package com.mosquito.project.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mosquito.project.dto.ShortenRequest;
import com.mosquito.project.persistence.entity.ShortLinkEntity;
import com.mosquito.project.service.ShortLinkService;
import com.mosquito.project.persistence.repository.LinkClickRepository;
import com.mosquito.project.persistence.repository.ApiKeyRepository;
import com.mosquito.project.security.IntrospectionResponse;
import com.mosquito.project.security.UserIntrospectionService;
import com.mosquito.project.support.TestAuthSupport;
import com.mosquito.project.web.UrlValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Optional;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(ShortLinkController.class)
class ShortLinkControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private ShortLinkService shortLinkService;

    @MockBean
    private LinkClickRepository linkClickRepository;

    @MockBean
    private ApiKeyRepository apiKeyRepository;

    @MockBean
    private StringRedisTemplate redisTemplate;

    @MockBean
    private UrlValidator urlValidator;

    @MockBean
    private UserIntrospectionService userIntrospectionService;

    @BeforeEach
    void setUpAuthStubs() {
        when(apiKeyRepository.findByKeyPrefix(TestAuthSupport.API_KEY_PREFIX))
            .thenReturn(Optional.of(TestAuthSupport.buildApiKeyEntity()));
        IntrospectionResponse response = new IntrospectionResponse();
        response.setActive(true);
        when(userIntrospectionService.introspect(any())).thenReturn(response);
    }

    @Test
    void shouldCreateShortLink_andReturn201() throws Exception {
        ShortLinkEntity e = new ShortLinkEntity();
        e.setCode("abc12345");
        e.setOriginalUrl("https://example.com/page");
        when(shortLinkService.create(anyString())).thenReturn(e);

        ShortenRequest req = new ShortenRequest();
        req.setOriginalUrl("https://example.com/page");

        mockMvc.perform(post("/api/v1/internal/shorten")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(req))
                .header("X-API-Key", TestAuthSupport.RAW_API_KEY))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.code").value("abc12345"))
            .andExpect(jsonPath("$.path").value("/r/abc12345"))
            .andExpect(jsonPath("$.originalUrl").value("https://example.com/page"));
    }

    @Test
    void shouldRedirect_whenCodeExists() throws Exception {
        ShortLinkEntity e = new ShortLinkEntity();
        e.setCode("abc12345");
        e.setOriginalUrl("https://example.com/page");
        when(shortLinkService.findByCode("abc12345")).thenReturn(Optional.of(e));
        when(urlValidator.isAllowedUrl("https://example.com/page")).thenReturn(true);

        mockMvc.perform(get("/r/abc12345"))
            .andExpect(status().isFound())
            .andExpect(header().string("Location", "https://example.com/page"));
    }

    @Test
    void redirect_shouldStillReturn302_whenClickSaveFails() throws Exception {
        ShortLinkEntity e = new ShortLinkEntity();
        e.setCode("fail1234");
        e.setOriginalUrl("https://example.com/fail");
        when(shortLinkService.findByCode("fail1234")).thenReturn(Optional.of(e));
        when(urlValidator.isAllowedUrl("https://example.com/fail")).thenReturn(true);
        when(linkClickRepository.save(any())).thenThrow(new RuntimeException("save failed"));

        mockMvc.perform(get("/r/fail1234"))
            .andExpect(status().isFound())
            .andExpect(header().string("Location", "https://example.com/fail"));
    }

    @Test
    void should404_whenCodeNotFound() throws Exception {
        when(shortLinkService.findByCode("nope")).thenReturn(Optional.empty());

        mockMvc.perform(get("/r/nope"))
            .andExpect(status().isNotFound());
    }

    @Test
    void shouldBlockMaliciousUrl() throws Exception {
        ShortLinkEntity e = new ShortLinkEntity();
        e.setCode("mal12345");
        e.setOriginalUrl("http://192.168.1.1/admin");
        when(shortLinkService.findByCode("mal12345")).thenReturn(Optional.of(e));
        when(urlValidator.isAllowedUrl("http://192.168.1.1/admin")).thenReturn(false);

        mockMvc.perform(get("/r/mal12345"))
            .andExpect(status().isBadRequest());
    }
}
