package cn.novalon.gym.manage.groupcourse.repository.impl;

import cn.novalon.gym.manage.groupcourse.converter.GroupCourseConverter;
import cn.novalon.gym.manage.groupcourse.dao.CourseLabelDao;
import cn.novalon.gym.manage.groupcourse.dao.CourseTypeLabelDao;
import cn.novalon.gym.manage.groupcourse.domain.CourseLabel;
import cn.novalon.gym.manage.groupcourse.entity.CourseLabelEntity;
import cn.novalon.gym.manage.groupcourse.entity.CourseTypeLabelEntity;
import cn.novalon.gym.manage.groupcourse.repository.ICourseLabelRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

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

@Repository
@Transactional
public class CourseLabelRepository implements ICourseLabelRepository {

    private static final Logger logger = LoggerFactory.getLogger(CourseLabelRepository.class);

    private final CourseLabelDao courseLabelDao;
    private final CourseTypeLabelDao courseTypeLabelDao;
    private final GroupCourseConverter converter;

    public CourseLabelRepository(CourseLabelDao courseLabelDao, CourseTypeLabelDao courseTypeLabelDao, 
                                  GroupCourseConverter converter) {
        this.courseLabelDao = courseLabelDao;
        this.courseTypeLabelDao = courseTypeLabelDao;
        this.converter = converter;
    }

    @Override
    public Mono<CourseLabel> findById(Long id) {
        return courseLabelDao.findByIdIsAndDeletedAtIsNull(id)
                .map(this::toCourseLabel);
    }

    @Override
    public Flux<CourseLabel> findAll() {
        return courseLabelDao.findAllByDeletedAtIsNull()
                .map(this::toCourseLabel);
    }

    @Override
    public Flux<CourseLabel> findByKeyword(String keyword) {
        if (keyword == null || keyword.isEmpty()) {
            return findAll();
        }
        return courseLabelDao.findByLabelNameContainingAndDeletedAtIsNull(keyword)
                .map(this::toCourseLabel);
    }

    @Override
    public Mono<CourseLabel> findByLabelName(String labelName) {
        return courseLabelDao.findByLabelNameAndDeletedAtIsNull(labelName)
                .map(this::toCourseLabel);
    }

    @Override
    public Mono<CourseLabel> save(CourseLabel courseLabel) {
        CourseLabelEntity entity = toCourseLabelEntity(courseLabel);
        return courseLabelDao.save(entity)
                .map(this::toCourseLabel);
    }

    @Override
    public Mono<CourseLabel> update(CourseLabel courseLabel) {
        return courseLabelDao.findByIdIsAndDeletedAtIsNull(courseLabel.getId())
                .switchIfEmpty(Mono.error(new RuntimeException("标签不存在")))
                .flatMap(existing -> {
                    existing.markNotNew();
                    if (courseLabel.getLabelName() != null) {
                        existing.setLabelName(courseLabel.getLabelName());
                    }
                    if (courseLabel.getColor() != null) {
                        existing.setColor(courseLabel.getColor());
                    }
                    if (courseLabel.getDescription() != null) {
                        existing.setDescription(courseLabel.getDescription());
                    }
                    existing.setUpdatedAt(LocalDateTime.now());
                    return courseLabelDao.save(existing);
                })
                .map(this::toCourseLabel);
    }

    @Override
    public Mono<Void> deleteById(Long id) {
        return courseLabelDao.softDelete(id, LocalDateTime.now())
                .then(courseTypeLabelDao.deleteByLabelId(id, LocalDateTime.now()))
                .then();
    }

    @Override
    public Flux<CourseLabel> findByTypeId(Long typeId) {
        return courseTypeLabelDao.findByTypeIdAndDeletedAtIsNull(typeId)
                .flatMap(typeLabel -> courseLabelDao.findByIdIsAndDeletedAtIsNull(typeLabel.getLabelId()))
                .map(this::toCourseLabel);
    }

    @Override
    public Mono<Void> addLabelsToType(Long typeId, List<Long> labelIds) {
        return Flux.fromIterable(labelIds)
                .flatMap(labelId -> {
                    return courseTypeLabelDao.physicalDeleteByTypeIdAndLabelId(typeId, labelId)
                            .then(Mono.defer(() -> {
                                CourseTypeLabelEntity entity = new CourseTypeLabelEntity();
                                entity.setTypeId(typeId);
                                entity.setLabelId(labelId);
                                return courseTypeLabelDao.save(entity).then(Mono.empty());
                            }));
                })
                .then();
    }

    @Override
    public Mono<Void> removeLabelFromType(Long typeId, Long labelId) {
        return courseTypeLabelDao.deleteByTypeIdAndLabelId(typeId, labelId, LocalDateTime.now())
                .then();
    }

    @Override
    public Mono<Void> clearLabelsFromType(Long typeId) {
        return courseTypeLabelDao.deleteByTypeId(typeId, LocalDateTime.now())
                .then();
    }

    private CourseLabel toCourseLabel(CourseLabelEntity entity) {
        if (entity == null) {
            return null;
        }
        CourseLabel label = new CourseLabel();
        label.setId(entity.getId());
        label.setLabelName(entity.getLabelName());
        label.setColor(entity.getColor());
        label.setDescription(entity.getDescription());
        label.setCreatedAt(entity.getCreatedAt());
        label.setUpdatedAt(entity.getUpdatedAt());
        return label;
    }

    private CourseLabelEntity toCourseLabelEntity(CourseLabel domain) {
        if (domain == null) {
            return null;
        }
        CourseLabelEntity entity = new CourseLabelEntity();
        entity.setId(domain.getId());
        entity.setLabelName(domain.getLabelName());
        entity.setColor(domain.getColor());
        entity.setDescription(domain.getDescription());
        if (domain.getId() != null) {
            entity.markNotNew();
        }
        return entity;
    }
}