package com.ruoyi.web.modules.booking.service.impl;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.booking.domain.AppBookingResourceQuery;
import com.ruoyi.web.modules.booking.domain.AppBookingSubmitBody;
import com.ruoyi.web.modules.booking.domain.vo.AppBookingDateBookedVo;
import com.ruoyi.web.modules.booking.domain.vo.AppBookingDateVo;
import com.ruoyi.web.modules.booking.domain.vo.AppointmentResourceVo;
import com.ruoyi.web.modules.booking.mapper.AppAppointmentResourceMapper;
import com.ruoyi.web.modules.booking.service.IAppBookingService;
import com.ruoyi.web.modules.booking.support.AppBookingResourceQuerySupport;
import com.ruoyi.web.modules.booking.support.AppBookingDateCheckSupport;
import com.ruoyi.web.modules.booking.support.AppBookingSubmitValidation;
import com.ruoyi.web.modules.booking.support.AppointmentBookingDateSupport;
import com.ruoyi.web.modules.booking.support.AppointmentResourceRules;
import com.ruoyi.web.modules.diagnosis.domain.BizServiceAppointment;
import com.ruoyi.web.modules.diagnosis.mapper.BizServiceAppointmentMapper;
import com.ruoyi.web.modules.diagnosis.support.ServiceAppointmentRules;
import com.ruoyi.web.modules.farming.domain.BizServiceAppointmentReview;
import com.ruoyi.web.modules.farming.mapper.BizServiceAppointmentReviewMapper;

/**
 * 移动端预约服务实现。
 */
@Service
public class AppBookingServiceImpl implements IAppBookingService
{
    @Autowired
    private AppAppointmentResourceMapper appAppointmentResourceMapper;

    @Autowired
    private BizServiceAppointmentMapper bizServiceAppointmentMapper;

    @Autowired
    private BizServiceAppointmentReviewMapper bizServiceAppointmentReviewMapper;

    @Override
    public List<AppBookingDateVo> selectBookingDateList()
    {
        return AppointmentBookingDateSupport.buildNextSevenDays(LocalDate.now());
    }

    @Override
    public List<AppointmentResourceVo> selectAppointmentResourceList(AppBookingResourceQuery query)
    {
        AppBookingResourceQuery effective = AppBookingResourceQuerySupport.normalize(query);
        List<AppointmentResourceVo> list = appAppointmentResourceMapper.selectAppointmentResourceList(effective);
        return list != null ? list : new ArrayList<>();
    }

    @Override
    public List<BizServiceAppointmentReview> selectExpertReviewList(Long expertResourceId)
    {
        if (expertResourceId == null)
        {
            throw new ServiceException("专家不存在");
        }
        AppointmentResourceVo expert = appAppointmentResourceMapper.selectAppointmentResourceByIdAndType(
                expertResourceId, AppointmentResourceRules.TYPE_EXPERT);
        if (expert == null)
        {
            throw new ServiceException("专家不存在或未发布");
        }
        List<BizServiceAppointmentReview> list =
                bizServiceAppointmentReviewMapper.selectReviewListByExpertProviderId(expertResourceId);
        return list != null ? list : new ArrayList<>();
    }

    @Override
    public AppBookingDateBookedVo checkBookedOnDate(Integer providerType, Long providerId, String appointDate,
            String createBy)
    {
        if (StringUtils.isEmpty(createBy))
        {
            throw new ServiceException("请先登录");
        }
        AppBookingDateCheckSupport.validate(providerType, providerId, appointDate);
        String dateStr = AppBookingDateCheckSupport.normalizeAppointDate(appointDate);
        int count = bizServiceAppointmentMapper.countMyActiveAppointmentOnDate(providerType, providerId, dateStr,
                createBy);
        AppBookingDateBookedVo vo = new AppBookingDateBookedVo();
        vo.setBooked(count > 0);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long submitAppointment(AppBookingSubmitBody body, String createBy)
    {
        AppBookingSubmitValidation.validateBody(body);
        LocalDate appointDate = AppBookingSubmitValidation.parseAppointDate(body.getAppointDate());
        AppointmentResourceVo resource = appAppointmentResourceMapper.selectAppointmentResourceByIdAndType(
                body.getResourceId(), body.getResourceType());
        if (resource == null)
        {
            throw new ServiceException("预约对象不存在或未发布");
        }
        AppBookingSubmitValidation.assertServiceWeekdayAllowed(resource, appointDate);
        assertOrgDailyCapacity(resource, appointDate);

        BizServiceAppointment row = new BizServiceAppointment();
        row.setProviderType(AppointmentResourceRules.resolveProviderType(body.getResourceType()));
        row.setProviderId(body.getResourceId());
        row.setAppointeeName(body.getAppointeeName());
        row.setAppointDate(java.sql.Date.valueOf(appointDate));
        row.setTimeStart(body.getTimeStart());
        row.setTimeEnd(body.getTimeEnd());
        row.setContactPhone(body.getContactPhone());
        row.setServiceAddress(body.getServiceAddress());
        row.setServiceRequirement(StringUtils.isNotEmpty(body.getServiceRequirement())
                ? body.getServiceRequirement() : null);
        row.setStatus(AppointmentResourceRules.resolveInitialStatus(body.getResourceType()));
        row.setCreateBy(createBy != null ? createBy : "");
        int n = bizServiceAppointmentMapper.insertBizServiceAppointment(row);
        if (n <= 0 || row.getId() == null)
        {
            throw new ServiceException("预约提交失败");
        }
        return row.getId();
    }

    private void assertOrgDailyCapacity(AppointmentResourceVo resource, LocalDate appointDate)
    {
        if (!AppointmentResourceRules.TYPE_ORG.equals(resource.getResourceType()))
        {
            return;
        }
        Integer max = resource.getMaxDailyAppointments();
        if (max == null || max <= 0)
        {
            return;
        }
        int count = bizServiceAppointmentMapper.countActiveAppointmentsOnDate(
                ServiceAppointmentRules.PROVIDER_TYPE_ORG, resource.getId(), appointDate.toString());
        if (count >= max)
        {
            throw new ServiceException("该机构当日预约已满");
        }
    }
}
