package dev.awesomedev.mirim.service;

import dev.awesomedev.mirim.domain.StudentProfile;
import dev.awesomedev.mirim.domain.User;
import dev.awesomedev.mirim.repository.StudentProfileRepository;
import dev.awesomedev.mirim.repository.UserRepository;
import dev.awesomedev.mirim.web.dto.ProfileResponse;
import dev.awesomedev.mirim.web.dto.ProfileUpdateRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.server.ResponseStatusException;

import java.time.Instant;
import java.time.LocalDate;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

/**
 * 이 파일이 하는 일:
 * "내 정보 수정"의 규칙을 검증한다 — 수정이 실제로 반영되는가(managed 엔티티),
 * 이메일 중복 방어, 프로필 없는 계정(멘토) 처리.
 */
class StudentProfileServiceTest {

    private StudentProfileRepository studentProfileRepository;
    private UserRepository userRepository;
    private StudentProfileService service;

    private User student;
    private StudentProfile profile;

    @BeforeEach
    void setUp() {
        studentProfileRepository = mock(StudentProfileRepository.class);
        userRepository = mock(UserRepository.class);
        service = new StudentProfileService(studentProfileRepository, userRepository,
                mock(TelegramNotifier.class));

        student = new User("student1", "해시", "임동혁", "STUDENT", "DEV");
        ReflectionTestUtils.setField(student, "id", 3L);
        profile = new StudentProfile(student, "donghyoek.lim", LocalDate.of(2008, 2, 27),
                "010-6485-4121", "시흥시/정왕2동", "미림고", "뉴미디어SW",
                "차정순", "모", "010-4316-6756", Instant.now());
    }

    private ProfileUpdateRequest request(String emailLocal, String address) {
        return new ProfileUpdateRequest("임동혁", emailLocal, LocalDate.of(2008, 2, 27),
                "010-6485-4121", address, "미림고", "뉴미디어SW",
                "차정순", "모", "010-4316-6756");
    }

    @Test
    @DisplayName("수정하면 이메일·주소가 실제로 바뀐다 (트랜잭션 안 재조회 = managed)")
    void update_actually_persists() {
        when(userRepository.findById(3L)).thenReturn(Optional.of(student));
        when(studentProfileRepository.findByUser(student)).thenReturn(Optional.of(profile));

        // 오타였던 이메일과 불완전한 주소를 고치는 시나리오 (실제 있었던 일!)
        ProfileResponse res = service.updateMyProfile(student,
                request("donghyeok.lim", "경기 시흥시 정왕2동 123, 45호"));

        assertEquals("donghyeok.lim", profile.getCompanyEmailLocal());
        assertEquals("경기 시흥시 정왕2동 123, 45호", profile.getAddress());
        assertEquals("donghyeok.lim@awesomedev.dev", res.fullEmail());
        // 트랜잭션 안에서 다시 조회했는가 (detached 함정 회귀 방지)
        verify(userRepository).findById(3L);
    }

    @Test
    @DisplayName("이메일을 남이 쓰는 값으로 바꾸려 하면 409, 내 값 그대로면 통과")
    void update_checks_email_duplication_only_when_changed() {
        when(userRepository.findById(3L)).thenReturn(Optional.of(student));
        when(studentProfileRepository.findByUser(student)).thenReturn(Optional.of(profile));
        when(studentProfileRepository.existsByCompanyEmailLocal("jiyu.im")).thenReturn(true);

        // 남이 쓰는 이메일 → 409
        ResponseStatusException ex = assertThrows(ResponseStatusException.class,
                () -> service.updateMyProfile(student, request("jiyu.im", "주소")));
        assertEquals(409, ex.getStatusCode().value());

        // 내 이메일 그대로 → 중복 검사 없이 통과 (existsBy가 내 것 때문에 true여도 막히면 안 됨)
        assertDoesNotThrow(() -> service.updateMyProfile(student, request("donghyoek.lim", "주소")));
        verify(studentProfileRepository, never()).existsByCompanyEmailLocal("donghyoek.lim");
    }

    @Test
    @DisplayName("프로필이 없는 계정(멘토)이 조회하면 404")
    void myProfile_throws_404_for_mentor() {
        User mentor = new User("mentor1", "해시", "멘토", "MENTOR", "DEV");
        when(studentProfileRepository.findByUser(mentor)).thenReturn(Optional.empty());

        ResponseStatusException ex = assertThrows(ResponseStatusException.class,
                () -> service.myProfile(mentor));
        assertEquals(404, ex.getStatusCode().value());
    }
}
