package com.devjob.controller;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
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.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.devjob.domain.Company;
import com.devjob.domain.Job;
import com.devjob.domain.JobStatus;
import com.devjob.domain.Member;
import com.devjob.domain.Role;
import com.devjob.repository.CompanyRepository;
import com.devjob.repository.JobRepository;
import com.devjob.repository.MemberRepository;
import com.devjob.security.CustomUserDetails;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.transaction.annotation.Transactional;

/**
 * EP24: 지원하기 API 통합 테스트.
 * 지원은 신원에 묶인 동작이므로 모든 엔드포인트가 인증을 요구한다(공개 동작 없음).
 */
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class ApplicationControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired private CompanyRepository companyRepository;
    @Autowired private MemberRepository memberRepository;
    @Autowired private JobRepository jobRepository;

    private Long jobId;
    private RequestPostProcessor applicant;

    @BeforeEach
    void setUp() {

        Company company = companyRepository.save(Company.builder().name("데브잡").location("서울").build());

        Member recruiter = memberRepository.save(
                Member.builder().email("recruiter@devjob.kr").password("pw").role(Role.USER).build());
        Member seeker = memberRepository.save(
                Member.builder().email("seeker@devjob.kr").password("pw").role(Role.USER).build());

        Job job = new Job();
        job.setCompany(company);
        job.setTitle("백엔드 개발자");
        job.setDescription("Spring Boot 모집");
        job.setSalary(5000);
        job.setStatus(JobStatus.OPEN);
        job.setRegisteredBy(recruiter.getId());   // 공고 작성자(채용자)
        jobId = jobRepository.save(job).getId();

        this.applicant = authentication(new UsernamePasswordAuthenticationToken(
                new CustomUserDetails(seeker), null, new CustomUserDetails(seeker).getAuthorities()));
    }

    @Test
    @DisplayName("로그인 회원이 공고 지원 → 201 Created")
    void applyAuthenticated() throws Exception {
        mockMvc.perform(post("/api/jobs/" + jobId + "/apply").with(applicant))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.success").value(true));
    }

    @Test
    @DisplayName("인증 없이 공고 지원 → 거부(4xx) — 지원은 항상 인증")
    void applyWithoutAuth() throws Exception {
        mockMvc.perform(post("/api/jobs/" + jobId + "/apply"))
                .andExpect(status().is4xxClientError());
    }

    @Test
    @DisplayName("인증 없이 내 지원 내역 조회 → 거부(4xx)")
    void myApplicationsWithoutAuth() throws Exception {
        mockMvc.perform(get("/api/members/me/applications"))
                .andExpect(status().is4xxClientError());
    }

    @Test
    @DisplayName("같은 공고 중복 지원 → 409 Conflict")
    void duplicateApply() throws Exception {
        mockMvc.perform(post("/api/jobs/" + jobId + "/apply").with(applicant))
                .andExpect(status().isCreated());

        mockMvc.perform(post("/api/jobs/" + jobId + "/apply").with(applicant))
                .andExpect(status().isConflict());
    }
}
