package com.bio.bio_backend.domain.admin.common_code.repository;

import com.bio.bio_backend.domain.admin.common_code.dto.GetCommonCodeResponseDto;
import com.bio.bio_backend.domain.admin.common_code.entity.CommonCode;
import com.bio.bio_backend.domain.admin.common_code.entity.QCommonCode;
import com.bio.bio_backend.domain.admin.common_code.entity.QCommonGroupCode;
import com.bio.bio_backend.domain.base.member.entity.QMember;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
@RequiredArgsConstructor
public class CommonCodeRepositoryImpl implements CommonCodeRepositoryCustom {

    private final JPAQueryFactory queryFactory;
    private final EntityManager entityManager;

    private final QCommonCode commonCode = QCommonCode.commonCode;
    private final QCommonGroupCode commonGroupCode = QCommonGroupCode.commonGroupCode;
    private final QMember member = QMember.member;

    @Override
    public List<CommonCode> findByGroupCode(String groupCode) {
        return queryFactory
                .selectFrom(commonCode)
                .where(commonCode.groupCode.eq(groupCode)
                        .and(commonCode.useFlag.eq(true)))
                .orderBy(commonCode.sortOrder.asc())
                .fetch();
    }

    @Override
    public List<CommonCode> findByParentCode(String parentCode) {
        return queryFactory
                .selectFrom(commonCode)
                .where(commonCode.parentCode.eq(parentCode)
                        .and(commonCode.useFlag.eq(true)))
                .orderBy(commonCode.sortOrder.asc())
                .fetch();
    }

    @Override
    public Optional<GetCommonCodeResponseDto> findCodeWithJoinInfo(String code) {
        return Optional.ofNullable(
            queryFactory
                .select(Projections.constructor(GetCommonCodeResponseDto.class,
                    commonCode.code, commonCode.name, commonCode.description,
                    commonCode.groupCode, commonCode.parentCode,
                    commonCode.characterRef1, commonCode.characterRef2, commonCode.characterRef3,
                    commonCode.characterRef4, commonCode.characterRef5,
                    commonCode.sortOrder, commonCode.useFlag,
                    commonCode.createdAt, commonCode.updatedAt,
                    commonGroupCode.name, member.name, member.name
                ))
                .from(commonCode)
                .leftJoin(commonGroupCode).on(commonCode.groupCode.eq(commonGroupCode.code))
                .leftJoin(member).on(commonCode.createdOid.eq(member.oid))
                .leftJoin(member).on(commonCode.updatedOid.eq(member.oid))
                .where(commonCode.code.eq(code))
                .fetchOne()
        );
    }

    @Override
    public Optional<GetCommonCodeResponseDto> findCodeWithJoinInfoNative(String code) {
        String nativeQuery = """
            SELECT cc.code, cc.name, cc.description, cc.group_code, cc.parent_code,
                   cc.character_ref1, cc.character_ref2, cc.character_ref3, cc.character_ref4, cc.character_ref5,
                   cc.sort_order, cc.use_flag, cc.created_at, cc.updated_at,
                   cgc.name as group_code_name, cm1.name as created_by_name, cm2.name as updated_by_name
            FROM st_common_code cc
            LEFT JOIN st_common_group_code cgc ON cc.group_code = cgc.code
            LEFT JOIN st_member cm1 ON cc.created_oid = cm1.oid
            LEFT JOIN st_member cm2 ON cc.updated_oid = cm2.oid
            WHERE cc.code = :code
            """;

        try {
            @SuppressWarnings("unchecked")
            List<GetCommonCodeResponseDto> results = entityManager
                .createNativeQuery(nativeQuery, "GetCommonCodeResponseDtoMapping")
                .setParameter("code", code)
                .getResultList();
            
            return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
        } catch (Exception e) {
            return Optional.empty();
        }
    }
}
