package com.example.his.api.front.service;

import cn.hutool.core.map.MapUtil;
import com.example.his.api.db.dao.AppointmentDao;
import com.example.his.api.db.dao.AppointmentRestrictionDao;
import com.example.his.api.db.dao.OrderDao;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("FrontAppointmentService")
public class AppointmentService {
    @Resource
    private AppointmentDao appointmentDao;

    @Resource
    private AppointmentRestrictionDao appointmentRestrictionDao;

    @Resource
    private OrderDao orderDao;

    @Resource
    private RedisTemplate redisTemplate;

    @Transactional
    public String insertFrontAppointment(Map entity) {
        HashMap<String, String> resultCode = new HashMap() {{
            put("full", "当天预约已满，请选择其他日期");
            put("fail", "预约失败");
            put("success", "预约成功");
        }};
        String key = "appointment#" + MapUtil.getStr(entity,"date");
        String execute = (String) redisTemplate.execute(new SessionCallback() {
            @Override
            public String execute(RedisOperations operations) throws DataAccessException {
                //关注缓存数据（拿到乐观锁的Version）
                operations.watch(key);
                //拿到缓存的数据
                Map entry = operations.opsForHash().entries(key);
                int maxNum = Integer.parseInt(entry.get("maxNum").toString());
                int realNum = Integer.parseInt(entry.get("realNum").toString());
                if (realNum < maxNum) {
                    //开启Redis事务
                    operations.multi();
                    //已人数+1
                    operations.opsForHash().increment(key, "realNum", 1);
                    //提交事务
                    List<Long> list = operations.exec();
                    if (list.size() == 0) {
                        return resultCode.get("fail");
                    }
                    long num = list.get(0);
                    return resultCode.get(num > 0 ? "success" : "fail");
                } else {
                    operations.unwatch();
                    return resultCode.get("full");
                }
            }
        });

        //如果Redis事务提交失败就结束Service方法
        if (!execute.equals(resultCode.get("success"))) {
            return execute;
        }

        int rows = appointmentDao.insertFrontAppointment(entity);
        if (rows != 1) {
            return resultCode.get("fail");
        }

        Map entry = redisTemplate.opsForHash().entries(key);
        int maxNum = Integer.parseInt(entry.get("maxNum").toString());

        HashMap param = new HashMap() {{
            put("date", MapUtil.getStr(entity,"date"));
            put("num_1", maxNum);
            put("num_2", maxNum);
            put("num_3", 1);
        }};

        //更新预约现流表中的预约人数
        rows = appointmentRestrictionDao.saveOrUpdateRealNum(param);
        if (rows == 0) {
            return resultCode.get("fail");
        }

        //更新订单状态
        int orderId = MapUtil.getInt(entity,"orderId");
        param = new HashMap() {{
            put("status", 5);
            put("id", orderId);
        }};
        rows = orderDao.updateAppointmentStatus(param);
        if (rows == 0) {
            return resultCode.get("fail");
        }

        return resultCode.get("success");
    }

    public PageInfo<HashMap> searchFrontAppointmentByPage(Map param) {
        PageHelper.startPage(MapUtil.getInt(param,"pageNum"),MapUtil.getInt(param,"pageSize"));
        ArrayList<HashMap> list = appointmentDao.searchFrontAppointmentByPage(param);
        PageInfo<HashMap> pageInfo = new PageInfo<>(list);
        return pageInfo;
    }

}

