package com.qqchen.deploy.backend.framework.service.impl;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.framework.query.DateRange;
import com.qqchen.deploy.backend.framework.query.Range;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.framework.utils.EntityPathResolver;
import com.qqchen.deploy.backend.framework.security.SecurityUtils;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.*;
import com.qqchen.deploy.backend.framework.exception.EntityNotFoundException;
import jakarta.persistence.OptimisticLockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

import java.io.Serializable;
import java.lang.reflect.Field;

import com.qqchen.deploy.backend.framework.annotation.ServiceType;

/**
 * 基础服务实现类
 *
 * @param <T> 实体类型
 * @param <D> DTO类型
 * @param <Q> 查询类型
 * @param <ID> ID类型
 */
@Transactional(readOnly = true)
@Slf4j
public abstract class BaseServiceImpl<T extends Entity<ID>, D extends BaseDTO, Q extends BaseQuery, ID extends Serializable> implements IBaseService<T, D, Q, ID> {

    protected IBaseRepository<T, ID> repository;

    protected BaseConverter<T, D> converter;

    protected EntityPath<T> entityPath;

    @PersistenceContext
    protected EntityManager entityManager;

    @Override
    @Transactional
    public D create(D dto) {
        validateDatabaseOperation("create");
        validateUniqueConstraints(dto);
        T entity = converter.toEntity(dto);
        T savedEntity = repository.save(entity);
        return converter.toDto(savedEntity);
    }

    @Override
    @Transactional
    public D update(ID id, D dto) {
        validateDatabaseOperation("update");
        T entity = findEntityById(id);
        converter.updateEntity(entity, dto);
        T updatedEntity = repository.save(entity);
        return converter.toDto(updatedEntity);
    }

    @Override
    @Transactional
    public void delete(ID id) {
        validateDatabaseOperation("delete");
        Class<T> entityClass = getEntityClass();
        LogicDelete logicDelete = entityClass.getAnnotation(LogicDelete.class);
        if (logicDelete != null && logicDelete.value()) {
            // 有LogicDelete注解且value为true，执行逻辑删除
            repository.logicDeleteById(id);
        } else {
            // 无LogicDelete注解或value为false，执行物理删除
            repository.deleteById(id);
        }
    }

    @Override
    public D findById(ID id) {
        validateDatabaseOperation("findById");
        T entity = findEntityById(id);
        return converter.toDto(entity);
    }

    @Override
    public List<D> findAll() {
        validateDatabaseOperation("findAll");
        List<T> entities = repository.findAll();
        return converter.toDtoList(entities);
    }

    @Override
    public List<D> findAll(Q query) {
        validateDatabaseOperation("findAll");
        if (query == null) {
            return findAll();
        }
        BooleanBuilder builder = createQueryPredicate(query);
        Sort sort = createSort(query);
        List<T> entities = repository.findAllByCondition(builder, sort);
        return converter.toDtoList(entities);
    }

    @Override
    public Page<D> page(Q query) {
        validateDatabaseOperation("page");
        Page<T> page = repository.findAll(createQueryPredicate(query), createPageRequest(query));
        return converter.toDtoPage(page);
    }

    protected BooleanBuilder createQueryPredicate(BaseQuery query) {
        BooleanBuilder builder = new BooleanBuilder().and(Expressions.asBoolean(true).isTrue());
        if (query != null) {
            buildQueryPredicate(query, builder);
        }
        handleSoftDelete(builder, query);
        return builder;
    }

    protected void handleSoftDelete(BooleanBuilder builder, BaseQuery query) {
        if (query == null || query.getDeleted() == null) {
            Class<T> entityClass = getEntityClass();
            LogicDelete logicDelete = entityClass.getAnnotation(LogicDelete.class);
            if (logicDelete != null && logicDelete.value()) {
                Path<?> deletedPath = EntityPathResolver.getPath(entityPath, "deleted");
                if (deletedPath instanceof BooleanPath) {
                    builder.and(((BooleanPath) deletedPath).eq(false));
                }
            }
        } else {
            Path<?> deletedPath = EntityPathResolver.getPath(entityPath, "deleted");
            if (deletedPath instanceof BooleanPath) {
                builder.and(((BooleanPath) deletedPath).eq(query.getDeleted()));
            }
        }
    }

    protected Sort createSort(BaseQuery query) {
        return query != null && StringUtils.hasText(query.getSortField()) ?
            Sort.by(Sort.Direction.fromString(query.getSortOrder()), query.getSortField()) :
            Sort.by(Sort.Direction.DESC, "createTime");
    }

    private void buildQueryPredicate(BaseQuery query, BooleanBuilder builder) {
        // 处理当前类的字段
        processClassFields(query, query.getClass(), builder);
    }

    private void processClassFields(Object query, Class<?> clazz, BooleanBuilder builder) {
        // 如果到达Object类或null，则停止递归
        if (clazz == null || clazz == Object.class) {
            return;
        }

        // 处理父类的字段
        processClassFields(query, clazz.getSuperclass(), builder);

        // 处理当前类的字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(query);
                if (value != null && StringUtils.hasText(value.toString())) {
                    QueryField queryField = field.getAnnotation(QueryField.class);
                    if (queryField != null) {
                        String fieldName = StringUtils.hasText(queryField.field()) ?
                            queryField.field() : field.getName();
                        Path<?> path = EntityPathResolver.getPath(entityPath, fieldName);
                        if (path != null) {
                            addCondition(builder, path, value, queryField.type());
                        }
                    }
                }
            } catch (Exception e) {
                // 忽略无法处理的字段
            }
        }
    }

    private void addCondition(BooleanBuilder builder, Path<?> path, Object value, QueryType queryType) {
        Predicate condition = null;

        if (path instanceof StringPath) {
            condition = createStringCondition((StringPath) path, value.toString(), queryType);
        } else if (path instanceof BooleanPath) {
            condition = ((BooleanPath) path).eq(Boolean.valueOf(value.toString()));
        } else if (path instanceof NumberPath) {
            condition = createNumberCondition((NumberPath<?>) path, value, queryType);
        } else if (path instanceof DateTimePath) {
            condition = createDateCondition((DateTimePath<?>) path, value, queryType);
        } else if (path instanceof EnumPath) {  // 添加对枚举类型的支持
            condition = ((EnumPath) path).eq(value);
        }

        if (condition != null) {
            builder.and(condition);
        }
    }

    private Predicate createStringCondition(StringPath path, String value, QueryType queryType) {
        switch (queryType) {
            case EQUAL:
                return path.eq(value);
            case LIKE:
                return path.containsIgnoreCase(value);
            case START_WITH:
                return path.startsWithIgnoreCase(value);
            case END_WITH:
                return path.endsWithIgnoreCase(value);
            case IN:
                if (value.contains(",")) {
                    List<String> values = Arrays.asList(value.split(","));
                    return path.in(values);
                }
                return path.eq(value);
            default:
                return null;
        }
    }

    @SuppressWarnings("unchecked")
    private <N extends Number & Comparable<?>> Predicate createNumberCondition(NumberPath<N> path, Object value, QueryType queryType) {
        N numValue = (N) parseNumber(value, path.getType());
        if (numValue != null) {
            switch (queryType) {
                case EQUAL:
                    return path.eq(numValue);
                case GREATER_THAN:
                    return path.gt(numValue);
                case LESS_THAN:
                    return path.lt(numValue);
                case GREATER_EQUAL:
                    return path.goe(numValue);
                case LESS_EQUAL:
                    return path.loe(numValue);
                case BETWEEN:
                    if (value instanceof Range) {
                        Range<?> range = (Range<?>) value;
                        N from = (N) parseNumber(range.getFrom(), path.getType());
                        N to = (N) parseNumber(range.getTo(), path.getType());
                        if (from != null && to != null) {
                            return path.between(from, to);
                        }
                    }
                default:
                    return null;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private <T extends Comparable<?>> Predicate createDateCondition(DateTimePath<T> path, Object value, QueryType queryType) {
        if (value instanceof DateRange range) {
            LocalDateTime from = range.getFrom();
            LocalDateTime to = range.getTo();

            switch (queryType) {
                case EQUAL:
                    return from != null ? path.eq((T) from) : null;
                case GREATER_THAN:
                    return from != null ? path.gt((T) from) : null;
                case LESS_THAN:
                    return to != null ? path.lt((T) to) : null;
                case BETWEEN:
                    if (from != null && to != null) {
                        return path.between((T) from, (T) to);
                    } else if (from != null) {
                        return path.goe((T) from);
                    } else if (to != null) {
                        return path.loe((T) to);
                    }
                default:
                    return null;
            }
        }
        return null;
    }

    private Number parseNumber(Object value, Class<?> targetType) {
        if (value == null) return null;
        try {
            String strValue = String.valueOf(value);
            if (targetType == Long.class) {
                return Long.valueOf(strValue);
            } else if (targetType == Integer.class) {
                return Integer.valueOf(strValue);
            } else if (targetType == Double.class) {
                return Double.valueOf(strValue);
            } else if (targetType == Float.class) {
                return Float.valueOf(strValue);
            } else if (targetType == BigDecimal.class) {
                return new BigDecimal(strValue);
            }
        } catch (NumberFormatException e) {
            // 忽略转换误
        }
        return null;
    }

    protected PageRequest createPageRequest(BaseQuery query) {
        if (query == null) {
            return PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "createTime"));
        }

        // 处理分页参数
        int pageNum = query.getPageNum() != null ? Math.max(1, query.getPageNum()) : 1;
        int pageSize = query.getPageSize() != null ? Math.max(1, Math.min(query.getPageSize(), 100)) : 10;

        // 处理排序
        Sort sort = StringUtils.hasText(query.getSortField()) ?
            Sort.by(Sort.Direction.fromString(query.getSortOrder()), query.getSortField()) :
            Sort.by(Sort.Direction.DESC, "createTime");

        return PageRequest.of(
            pageNum - 1,  // 转换为从0开始的页码
            pageSize,
            sort
        );
    }

    @Transactional(
        readOnly = false,
        propagation = Propagation.REQUIRES_NEW
    )
    @Override
    public void batchProcess(List<D> dtos) {
        LocalDateTime now = LocalDateTime.now();
        String operator = SecurityUtils.getCurrentUsername();

        List<T> entities = converter.toEntityList(dtos);
        Lists.partition(entities, 500).forEach(batch -> {
            try {
                // 加锁查询最新数据
                List<T> currentEntities = batch.stream()
                    .map(e -> repository.findById(e.getId())
                        .orElseThrow(() -> new EntityNotFoundException(getEntityClass().getSimpleName(), e.getId())))
                    .collect(Collectors.toList());

                // 版本检查
                for (int i = 0; i < batch.size(); i++) {
                    currentEntities.get(i).checkVersion(batch.get(i).getVersion());
                }

                // 批量更新
                List<ID> ids = currentEntities.stream()
                    .map(Entity::getId)
                    .collect(Collectors.toList());

                repository.batchUpdate(ids, now, operator);
                repository.flush();
                entityManager.clear();
            } catch (OptimisticLockException e) {
                log.error("Batch update failed for batch size: {}", batch.size(), e);
                throw e;
            }
        });
    }

    @Transactional(readOnly = false)
    @Retryable(
        value = {OptimisticLockException.class},
        maxAttempts = 3,
        backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    public D updateWithRetry(ID id, D dto) {
        try {
            return update(id, dto);
        } catch (OptimisticLockException e) {
            entityManager.refresh(findEntityById(id));
            throw e;
        }
    }

    @Override
    @Transactional
    public T findByIdWithLock(ID id) {
        validateDatabaseOperation("findByIdWithLock");
        return repository.findByIdWithLock(id)
            .orElseThrow(() -> new EntityNotFoundException(getEntityClass().getSimpleName(), id));
    }

    @Override
    @SuppressWarnings("unchecked")
    public Class<T> getEntityClass() {
        Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(getClass(), BaseServiceImpl.class);
        if (genericTypes != null && genericTypes.length > 0) {
            return (Class<T>) genericTypes[0];
        }
        throw new IllegalStateException("Could not resolve entity class");
    }

    @Override
    public T findEntityById(ID id) {
        validateDatabaseOperation("findEntityById");
        return repository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException(getEntityClass().getSimpleName(), id));
    }

    protected void validateDatabaseOperation(String operation) {
        ServiceType serviceType = getClass().getAnnotation(ServiceType.class);
        if (serviceType != null && serviceType.value() == ServiceType.Type.INTEGRATION) {
            throw new UnsupportedOperationException(
                "Database operation '" + operation + "' is not supported in integration service");
        }
    }

    /**
     * 校验唯一约束
     * 子类可以重写此方法实现具体的唯一性检查
     */
    protected void validateUniqueConstraints(D dto) {
        // 默认不做任何检查
    }
}