package com.bio.bio_backend.domain.user.member.repository;

import com.bio.bio_backend.domain.user.member.entity.Member;
import com.bio.bio_backend.domain.user.member.entity.QMember;
import com.bio.bio_backend.domain.user.member.enums.MemberRole;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * QueryDSL을 활용하여 MemberRepositoryCustom 인터페이스를 구현하는 클래스
 * 복잡한 쿼리나 동적 쿼리를 QueryDSL로 작성하여 성능과 가독성을 향상시킵니다.
 */
@Repository
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    /**
     * QMember 인스턴스를 생성하여 쿼리에서 사용합니다.
     * QueryDSL의 Q클래스를 통해 타입 안전한 쿼리 작성이 가능합니다.
     */
    private final QMember member = QMember.member;

    @Override
    public Optional<Member> findByUserIdCustom(String userId) {
        // QueryDSL을 사용하여 사용자 ID로 회원을 조회합니다.
        // eq() 메서드를 사용하여 정확한 일치 조건을 설정합니다.
        Member foundMember = queryFactory
                .selectFrom(member)
                .where(member.userId.eq(userId))
                .fetchOne();

        return Optional.ofNullable(foundMember);
    }

    @Override
    public List<Member> findByRole(String role) {
        // 역할별로 회원을 조회합니다.
        // String을 MemberRole enum으로 변환하여 비교합니다.
        return queryFactory
                .selectFrom(member)
                .where(member.role.eq(MemberRole.fromValue(role)))
                .fetch();
    }

    @Override
    public List<Member> findByUseFlag(Boolean useFlag) {
        // 사용 여부별로 회원을 조회합니다.
        return queryFactory
                .selectFrom(member)
                .where(member.useFlag.eq(useFlag))
                .fetch();
    }

    @Override
    public Optional<Member> findByUserIdAndUseFlag(String userId, Boolean useFlag) {
        // 사용자 ID와 사용 여부를 모두 만족하는 회원을 조회합니다.
        // and() 메서드를 사용하여 여러 조건을 결합합니다.
        Member foundMember = queryFactory
                .selectFrom(member)
                .where(member.userId.eq(userId)
                        .and(member.useFlag.eq(useFlag)))
                .fetchOne();

        return Optional.ofNullable(foundMember);
    }

    @Override
    public Page<Member> findMembersByCondition(String userId, String role, Boolean useFlag, Pageable pageable) {
        // BooleanBuilder를 사용하여 동적 쿼리를 구성합니다.
        // null이 아닌 조건만 쿼리에 포함시킵니다.
        BooleanBuilder builder = new BooleanBuilder();

        // 사용자 ID가 제공된 경우 부분 검색 조건을 추가합니다.
        if (userId != null && !userId.trim().isEmpty()) {
            builder.and(member.userId.containsIgnoreCase(userId));
        }

        // 역할이 제공된 경우 정확한 일치 조건을 추가합니다.
        if (role != null && !role.trim().isEmpty()) {
            builder.and(member.role.eq(MemberRole.fromValue(role)));
        }

        // 사용 여부가 제공된 경우 정확한 일치 조건을 추가합니다.
        if (useFlag != null) {
            builder.and(member.useFlag.eq(useFlag));
        }

        // 전체 개수를 조회합니다.
        long total = queryFactory
                .selectFrom(member)
                .where(builder)
                .fetchCount();

        // 페이징 조건을 적용하여 결과를 조회합니다.
        List<Member> content = queryFactory
                .selectFrom(member)
                .where(builder)
                .orderBy(member.createdAt.desc()) // 생성일 기준 내림차순 정렬
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        // Page 객체를 생성하여 반환합니다.
        return new PageImpl<>(content, pageable, total);
    }

    @Override
    public List<Member> findActiveMembersByLastLogin(LocalDateTime lastLoginAfter) {
        // 마지막 로그인 시간이 특정 시간 이후인 활성 회원들을 조회합니다.
        // 여러 조건을 조합하여 복잡한 쿼리를 작성합니다.
        return queryFactory
                .selectFrom(member)
                .where(member.useFlag.eq(true) // 사용 중인 상태
                        .and(member.lastLoginAt.isNotNull()) // 마지막 로그인 시간이 존재
                        .and(member.lastLoginAt.after(lastLoginAfter))) // 특정 시간 이후
                .orderBy(member.lastLoginAt.desc()) // 마지막 로그인 시간 기준 내림차순 정렬
                .fetch();
    }
}
