package com.chuanxia.mcp.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chuanxia.mcp.bean.dto.DomainBindDto;
import com.chuanxia.mcp.bean.dto.DomainQueryDto;
import com.chuanxia.mcp.bean.enums.DefaultDomainEnums;
import com.chuanxia.mcp.bean.enums.DeviceUaLevel;
import com.chuanxia.mcp.bean.model.*;
import com.chuanxia.mcp.bean.vo.Result;
import com.chuanxia.mcp.exception.ResultException;
import com.chuanxia.mcp.mapper.*;
import com.chuanxia.mcp.service.CdnDoaminService;
import com.chuanxia.mcp.service.CdnDomainCityService;
import com.chuanxia.mcp.service.UserService;
import com.chuanxia.mcp.util.IpUtil;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author Administrator
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CdnDoaminServiceImpl extends ServiceImpl<CdnDomainMapper, CdnDomain> implements CdnDoaminService {
    private final UserService userService;
    private final CdnDomainMapper domainMapper;
    private final CdnDomainCityService domainCityService;
    private final CdnUaMapper cdnUaMapper;
    private final CdnDomainOperatorMapper domainOperatorMapper;
    private final CdnDomainCityMapper domainCityMapper;
    private final CdnDomainTimeRuleMapper domainTimeRuleMapper;
    private final CdnCityMapper cityMapper;
    private final VideoM3u8Mapper m3u8Mapper;

    @Override
    public Result<IPage<CdnDomain>> listPage(DomainQueryDto queryDto) {
        IPage<CdnDomain> page = new Page<>(queryDto.getPageNum(), queryDto.getPageSize());
        LambdaQueryWrapper<CdnDomain> query = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(queryDto.getDomain())) {
            query.like(CdnDomain::getDomain, queryDto.getDomain());
        }
        if (StringUtils.isNotBlank(queryDto.getManufacturer())) {
            query.like(CdnDomain::getManufacturer, queryDto.getManufacturer());
        }
        page = domainMapper.selectPage(page, query);
        return Result.success(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<String> saveDomain(CdnDomain domain) {

        SystemUser loginUser = userService.getLoginUser();
        if (StringUtils.isBlank(domain.getDomain())) {
            throw new ResultException(500, "域名不能为空");
        }
        if (StringUtils.isBlank(domain.getManufacturer())) {
            throw new ResultException(500, "厂商不能为空");
        }
        long count = this.count();
        if (count <= 0) {
            domain.setDefaultDomain(1);
        } else {
            domain.setDefaultDomain(0);
        }
        domain.setCreateBy(loginUser.getId());
        this.save(domain);
        return Result.success("操作成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<String> bindCdn(DomainBindDto bindDto) {
        Integer domainId = bindDto.getDomainId();
        CdnDomain domain = this.getById(domainId);
        if (domain == null) {
            throw new ResultException(500, "当前域名id不存在");
        }
        if (CollectionUtil.isEmpty(bindDto.getCityIds())) {
            throw new ResultException(500, "城市列表不能为空");
        }
        List<Integer> cityIds = bindDto.getCityIds();
        List<CdnDomainCity> domainCityList = new ArrayList<>(cityIds.size());
        for (Integer cityId : cityIds) {
            CdnCity cdnCity = cityMapper.selectById(cityId);
            CdnDomainCity domainCity = new CdnDomainCity();
            domainCity.setCname(cdnCity.getCname());
            domainCity.setCityId(cityId);
            domainCity.setDomain(domain.getDomain());
            domainCity.setDomainId(domainId);
            domainCityList.add(domainCity);
        }
        LambdaQueryWrapper<CdnDomainCity> del = Wrappers.lambdaQuery();
        del.eq(CdnDomainCity::getDomainId, domainId);
        domainCityService.remove(del);
        domainCityService.saveBatch(domainCityList);
        return Result.success("操作成功");
    }

    @Override
    public Result<String> updateDomain(CdnDomain domain) {
        Integer defaultDomain = domain.getDefaultDomain();
        if (DefaultDomainEnums.DEFAULTDOMAIN.getStatus().equals(defaultDomain)) {
            //判断是否已存在默认域名,存在则不允许添加了
            LambdaQueryWrapper<CdnDomain> query = Wrappers.lambdaQuery();
            query.eq(CdnDomain::getDefaultDomain, defaultDomain).ne(CdnDomain::getId, domain.getId());
            long count = this.count(query);
            if (count >= 1) {
                throw new ResultException(500, "已存在默认域名,不能再次添加默认域名");
            }
        }
        this.updateById(domain);

        domainOperatorMapper.updateDomainByDomainId(domain.getId(), domain.getDomain());
        domainCityMapper.updateByDomainId(domain.getId(), domain.getDomain());
        domainTimeRuleMapper.updateDomainByDomainId(domain.getId(), domain.getDomain());

        return Result.success("操作成功");
    }

    @Override
    public Result getCdnByVideoId(String videoId, BigDecimal internetSpeed, String operatorName, String type, HttpServletRequest request) {
        //根据ip获取归属地
        String requestIp = IpUtil.getRequestIp(request);
        try {
            requestIp = "27.37.0.1";
            String cityInfo = IpUtil.getCityInfo(requestIp);
            CdnDomain cdnDomain = new CdnDomain();
            if (StringUtils.isNotBlank(cityInfo)) {
                String[] split = cityInfo.split("\\|");
                if (split.length >= 5) {
                    //根据城市名获取域名信息
                    String city = split[3].replaceAll("市", "");
                    cdnDomain = getDomainByCityName(city);
                    if (cdnDomain == null) {
                        String operator = split[4];
                        if (StringUtils.isBlank(operator)) {
                            //根据城市没有获取到域名,根据运营商获取域名信息
                            operator = operatorName;
                        }
                        cdnDomain = getDomainByOperator(operator);
                        if (cdnDomain == null) {
                            //根据运营商没有获取到域名,根据时间区间获取域名信息
                            cdnDomain = getDomainByTimeRule();
                            if (cdnDomain == null) {
                                //根据时间没有获取到域名,获取默认域名
                                LambdaQueryWrapper<CdnDomain> query = Wrappers.lambdaQuery();
                                query.eq(CdnDomain::getDefaultDomain, 1);
                                cdnDomain = domainMapper.selectOne(query);
                            }
                        }
                    }
                }
                //获取ua
                String header = request.getHeader("user-agent");
                UserAgent userAgent = UserAgent.parseUserAgentString(header);
                CdnUa uaByConfig = getUaByConfig(userAgent);
                String ratio = "1024*768";
                if (uaByConfig != null) {
                    //根据配置+网速,生成对应分辨率
                    Integer level = uaByConfig.getLevel();
                    //计算分辨率
                    level = countLevel(level, internetSpeed);
                    if (DeviceUaLevel.HIGH.getLevel().equals(level)) {

                        ratio = "1980x1080";
                    } else if (DeviceUaLevel.MEDIUM.getLevel().equals(level)) {
                        ratio = "1280x720";
                    } else if (DeviceUaLevel.LOW.getLevel().equals(level)) {
                        ratio = "1024x768";
                    }
                }
                LambdaQueryWrapper<VideoM3u8> m3u8Query = Wrappers.lambdaQuery();
                m3u8Query.eq(VideoM3u8::getVideoId, videoId);
                List<VideoM3u8> videoM3u8s = m3u8Mapper.selectList(m3u8Query);
                if (CollectionUtil.isEmpty(videoM3u8s)) {
                    throw new ResultException(500, "当前videoId没有找到对应视频");
                }
                VideoM3u8 m3u8 = null;
                for (VideoM3u8 videoM3u8 : videoM3u8s) {
                    if (ratio.equals(videoM3u8.getResolution())) {
                        m3u8 = videoM3u8;
                        break;
                    }
                }
                if (m3u8 == null) {
                    m3u8 = videoM3u8s.get(0);
                }
                if (!StringUtils.isBlank(type)) {
                    String downLoadFileUrl = "http://127.0.0.1:8088/api/cdnDomain/downLoadM3u8File?cdn=" + cdnDomain.getDomain() + "&id=" + m3u8.getId();
                    throw new ResultException(888, downLoadFileUrl);
                }
                String tsListStr = m3u8.getTsListStr();
                String tsName = m3u8.getTS_NAME();

                if (!StringUtils.isBlank(tsListStr)) {
                    if (StringUtils.isBlank(tsName)) {
                        List<VideoTs> tsList = JSON.parseArray(tsListStr, VideoTs.class);
                        //说明是有序的视频,则不需要返回tsList
                        m3u8.setTS_LIST(tsList);
                    }
                    String prefixPath = m3u8.getPREFIX_PATH();
                    if (StringUtils.isNotBlank(prefixPath)) {
                        m3u8.setPREFIX_PATH(cdnDomain.getDomain() + m3u8.getPREFIX_PATH());
                    }
                    m3u8.setTsListStr("");
                } else {
                    m3u8.setPREFIX_PATH(cdnDomain.getDomain() + m3u8.getPREFIX_PATH());
                }
                return Result.success(m3u8);
            } else {
                throw new ResultException(500, "根据ip地址获取城市信息失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ResultException(500, e.getMessage());
        }
    }

    @Override
    public Result downLoadM3u8File(String cdn, Integer id, HttpServletResponse response) {
        VideoM3u8 m3u8 = m3u8Mapper.selectById(id);
        if (m3u8 == null) {
            throw new ResultException(500, "当前视频不存在");
        }
        response.setContentType("text/plain");
        response.setHeader("Content-Disposition", "attachment;filename=" + id + ".m3u8");
        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
            StringBuilder builder = new StringBuilder();
            String last = "\n";
            builder.append("#EXTM3U").append(last);
            builder.append("#EXT-X-VERSION:").append(m3u8.getVERSION()).append(last);
            builder.append("#EXT-X-TARGETDURATION:").append(m3u8.getTARGETDURATION()).append(last);
            builder.append("#EXT-X-MEDIA-SEQUENCE:").append(m3u8.getSEQUENCE()).append(last);
            builder.append("#EXT-X-PLAYLIST-TYPE:").append(m3u8.getTYPE()).append(last);
            if (StringUtils.isNotBlank(m3u8.getKEY())) {
                builder.append("#EXT-X-KEY:METHOD=AES-128,URI=\"data:application/octet-stream;base64,").append(m3u8.getKEY()).append("\"").
                        append(",IV=").append(m3u8.getIV()).append(last);
            }
            String tsListStr = m3u8.getTsListStr();
            if (StringUtils.isNotBlank(tsListStr)) {
                List<VideoTs> tsList = JSON.parseArray(tsListStr, VideoTs.class);
                String prefixPath = m3u8.getPREFIX_PATH();
                boolean notBlank = StringUtils.isNotBlank(prefixPath);

                for (VideoTs videoTs : tsList) {
                    String name = videoTs.getName();
                    builder.append("#EXTINF:").append(videoTs.getTime()).append(",").append(last);
                    if (notBlank) {
                        //为true表示不为空,说明ts地址是相对路径,这里拼接cdn地址
                        builder.append(cdn).append(prefixPath).append(name).append(last);
                    } else {
                        builder.append(name).append(last);
                    }
                }
            }
            builder.append("#EXT-X-ENDLIST").append(last);
            out.write(builder.toString().getBytes());
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Integer countLevel(Integer level, BigDecimal internetSpeed) {
        //网速分为三个档位
        Integer speedLevel = 1;
        if (internetSpeed.doubleValue() <= 100) {
            speedLevel = 3;
        } else if (100 < internetSpeed.doubleValue() && internetSpeed.doubleValue() < 500) {
            speedLevel = 2;
        }
        if (level > speedLevel) {
            return speedLevel;
        } else {
            return level;
        }
    }

    /**
     * 根据城市信息获取cdn
     *
     * @param city 城市名
     * @return cdn
     */
    private CdnDomain getDomainByCityName(String city) {
        log.info("根据城市名获取cdn,当前城市:{}", city);
        return domainMapper.getDomainByCityName(city);
    }

    /**
     * 根据
     *
     * @param operator 运营商
     * @return cdn
     */
    private CdnDomain getDomainByOperator(String operator) {
        log.info("根据运营商获取cdn,当前运营商:{}", operator);
        return domainMapper.getDomainByOperator(operator);
    }

    /**
     * 根据当前时间获取cdn
     *
     * @return cdn
     */
    private CdnDomain getDomainByTimeRule() {
        Calendar cal = Calendar.getInstance();
        int hours = cal.get(Calendar.HOUR_OF_DAY);
        log.info("根据时间获取cdn,当前小时:{}", hours);
        return domainMapper.getDomainByTimeRule(hours);
    }

    /**
     * 根据当前浏览器ua,获取对应配置
     *
     * @param userAgent
     * @return
     */
    private CdnUa getUaByConfig(UserAgent userAgent) {
        LambdaQueryWrapper<CdnUa> query = Wrappers.lambdaQuery();
        //获取浏览器对象
        Browser browser = userAgent.getBrowser();
        //获取操作系统对象
        OperatingSystem operatingSystem = userAgent.getOperatingSystem();
        Browser group = browser.getGroup();
        String name = group.getName();
        if (StringUtils.isNotBlank(name)) {
            query.eq(CdnUa::getBrowser, name);
        }
        String version = userAgent.getBrowserVersion().getVersion();
        if (StringUtils.isNotBlank(version)) {
            query.eq(CdnUa::getBrowserVersion, version);
        }
        String os = operatingSystem.getGroup().getName();
        if (StringUtils.isNotBlank(os)) {
            query.eq(CdnUa::getOs, os);
        }
        log.info("获取到用户浏览器:{},浏览器版本:{},操作系统:{}", name, version, os);
        query.orderByDesc(CdnUa::getCreateTime);
        query.last("limit 0,1");
        System.out.println("浏览器名:" + browser.getName());
        System.out.println("浏览器类型:" + browser.getBrowserType());
        System.out.println("浏览器家族:" + browser.getGroup());
        System.out.println("浏览器生产厂商:" + browser.getManufacturer());
        System.out.println("浏览器使用的渲染引擎:" + browser.getRenderingEngine());
        System.out.println("浏览器版本:" + userAgent.getBrowserVersion());
        System.out.println("操作系统名:" + operatingSystem.getName());
        System.out.println("访问设备类型:" + operatingSystem.getDeviceType());
        System.out.println("操作系统家族:" + operatingSystem.getGroup());
        System.out.println("操作系统生产厂商:" + operatingSystem.getManufacturer());

        return cdnUaMapper.selectOne(query);
    }
}
