package com.bio.bio_backend.domain.base.member.service;

import com.bio.bio_backend.domain.base.member.dto.MemberDto;
import com.bio.bio_backend.domain.base.member.dto.GetMemberResponseDto;
import com.bio.bio_backend.domain.base.member.dto.MemberSearchCondition;
import com.bio.bio_backend.global.dto.PagedResult;
import com.bio.bio_backend.domain.base.member.entity.Member;
import com.bio.bio_backend.domain.base.member.mapper.MemberMapper;
import com.bio.bio_backend.domain.base.member.repository.MemberRepository;
import com.bio.bio_backend.global.exception.ApiException;
import com.bio.bio_backend.global.constants.ApiResponseCode;
import com.bio.bio_backend.global.constants.AppConstants;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
@Slf4j
@Transactional(readOnly = true)
public class MemberServiceImpl implements MemberService {

    private final MemberMapper memberMapper;  // MapStruct Mapper 사용
    private final MemberRepository memberRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
        Member member = memberRepository.findActiveMemberByUserId(id)
                .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + id));
        return memberMapper.toMemberDto(member);
    }

    @Override
    @Transactional
    public MemberDto createMember(MemberDto memberDto) {
        // userId 중복 체크
        if (memberRepository.existsByUserId(memberDto.getUserId())) {
            throw new ApiException(ApiResponseCode.USER_ID_DUPLICATE);
        }
        
        Member member = Member.builder()
                .userId(memberDto.getUserId())
                .password(bCryptPasswordEncoder.encode(memberDto.getPassword()))
                .name(memberDto.getName())
                .email(memberDto.getEmail())
                .build();
        
        member.setCreatedOid(AppConstants.ADMIN_OID);
        member.setCreatedId(AppConstants.ADMIN_USER_ID);
        member.setUpdatedOid(AppConstants.ADMIN_OID);
        member.setUpdatedId(AppConstants.ADMIN_USER_ID);
        
        Member savedMember = memberRepository.save(member);
        
        return memberMapper.toMemberDto(savedMember);
    }

    @Override
    @Transactional
    public void updateMember(MemberDto memberDto) {
        Member member = memberRepository.findActiveMemberByUserId(memberDto.getUserId())
                .orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
        
        memberMapper.updateMemberFromDto(memberDto, member);
        memberRepository.save(member);
    }

    @Override
    public String getRefreshToken(String id) {
        Member member = memberRepository.findActiveMemberByUserId(id)
                .orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
        
        return member.getRefreshToken();
    }

    @Override
    @Transactional
    public void deleteRefreshToken(String id) {
        Member member = memberRepository.findActiveMemberByUserId(id)
                .orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
        
        member.setRefreshToken(null);
        memberRepository.save(member);
    }
    
    @Override
    public List<GetMemberResponseDto> getMembers(MemberSearchCondition condition) {
        List<Member> members = memberRepository.findMembers(condition);
        
        return memberMapper.toGetMemberResponseDtoList(members);
    }
    
    @Override
    public PagedResult<GetMemberResponseDto> getMembersPaged(MemberSearchCondition condition, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        Page<Member> memberPage = memberRepository.findMembers(condition, pageable);
        List<GetMemberResponseDto> members = memberMapper.toGetMemberResponseDtoList(memberPage.getContent());

        return PagedResult.of(memberPage, members);
    }
}
