package org.jeecg.modules.adweb.seo.controller;

import static cn.hutool.core.collection.CollUtil.distinct;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xkcoding.http.util.StringUtil;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.adweb.common.constant.AdwebConstant;
import org.jeecg.modules.adweb.seo.dto.AvesApiSearchKeywordsDTO;
import org.jeecg.modules.adweb.seo.dto.ChangeTypeDTO;
import org.jeecg.modules.adweb.seo.entity.SeoKeywords;
import org.jeecg.modules.adweb.seo.entity.SeoPlanSubscription;
import org.jeecg.modules.adweb.seo.service.ISeoKeywordsRankService;
import org.jeecg.modules.adweb.seo.service.ISeoKeywordsService;
import org.jeecg.modules.adweb.seo.service.ISeoPlanSubscriptionService;
import org.jeecg.modules.adweb.site.entity.AdwebSite;
import org.jeecg.modules.adweb.site.service.IAdwebSiteService;
import org.jeecg.modules.adweb.system.service.SysAdwebApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Description: 关键词
 * @Author: jeecg-boot
 * @Date: 2024-10-15
 * @Version: V1.0
 */
@Tag(name = "关键词")
@RestController
@RequestMapping("/seo/seoKeywords")
@Slf4j
public class SeoKeywordsController extends JeecgController<SeoKeywords, ISeoKeywordsService> {
    @Autowired private ISeoKeywordsService seoKeywordsService;

    @Autowired ISeoKeywordsRankService seoKeywordsRankService;

    @Autowired ISeoKeywordsRankService seoKpiStatisticsService;

    @Autowired private SysAdwebApi sysAdwebApi;

    @Autowired private IAdwebSiteService adwebSiteService;

    @Autowired private ISeoPlanSubscriptionService seoPlanSubscriptionService;

    @Value("${jeecg.path.upload}")
    private String uploadUrl;

    /**
     * 分页列表查询
     *
     * @param seoKeywords
     * @param pageNo
     * @param pageSize
     * @param req
     * @return
     */
    // @AutoLog(value = "关键词-分页列表查询")
    @Operation(summary = "关键词-分页列表查询")
    @GetMapping(value = "/list")
    public Result<IPage<SeoKeywords>> queryPageList(
            SeoKeywords seoKeywords,
            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
            HttpServletRequest req) {
        if ((seoKeywords.getSiteCode() == null || seoKeywords.getSiteCode().isEmpty())) {
            return Result.OK();
        }
        LambdaQueryWrapper<SeoKeywords> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtil.isNotEmpty(seoKeywords.getKeywords())) {
            queryWrapper.like(SeoKeywords::getKeywords, "%" + seoKeywords.getKeywords() + "%");
        }
        //        queryWrapper.eq("related_keyword_id", seoKeywords.getRelatedKeywordId());
        //        queryWrapper.eq("subscription_id", seoKeywords.getSubscriptionId());
        queryWrapper.eq(SeoKeywords::getSiteCode, seoKeywords.getSiteCode());
        if (seoKeywords.getKeywordType() != null) {
            queryWrapper.eq(SeoKeywords::getKeywordType, seoKeywords.getKeywordType());
        }
        queryWrapper.ne(SeoKeywords::getStatus, 0);
        queryWrapper
                .orderByAsc(SeoKeywords::getPriority)
                .orderByDesc(SeoKeywords::getOptimizeProcess)
                .orderByDesc(SeoKeywords::getCreateTime);
        Page<SeoKeywords> page = new Page<SeoKeywords>(pageNo, pageSize);
        IPage<SeoKeywords> pageList = seoKeywordsService.page(page, queryWrapper);
        return Result.OK(pageList);
    }

    /**
     * 添加
     *
     * @param seoKeywords
     * @return
     */
    @AutoLog(value = "关键词-添加")
    @Operation(summary = "关键词-添加")
    @PreAuthorize("@jps.requiresPermissions('keywords:seo_keywords:add')")
    @PostMapping(value = "/add")
    public Result<String> add(@RequestBody SeoKeywords seoKeywords) {
        //        AdwebSite siteByCode = adwebSiteService.getSiteByCode(seoKeywords.getSiteCode());
        //        if (siteByCode == null) {
        //            return Result.error("未找到对应站点");
        //        }
        seoKeywords.setAppKey(AdwebConstant.APPKEY);
        // 根据siteCode获取站点域名
        //        String domain = siteByCode.getDomain();
        //        seoKeywords.setDomain(domain);
        seoKeywords.setStatus(1);
        //        seoKeywords.setLang("en");
        seoKeywordsService.save(seoKeywords);
        return Result.OK("添加成功！");
    }

    /**
     * 编辑
     *
     * @param seoKeywords
     * @return
     */
    @AutoLog(value = "关键词-编辑")
    @Operation(summary = "关键词-编辑")
    @PreAuthorize("@jps.requiresPermissions('keywords:seo_keywords:edit')")
    @RequestMapping(
            value = "/edit",
            method = {RequestMethod.PUT, RequestMethod.POST})
    public Result<String> edit(@RequestBody SeoKeywords seoKeywords) {
        seoKeywordsService.updateById(seoKeywords);
        return Result.OK("编辑成功!");
    }

    /**
     * 通过id删除
     *
     * @param id
     * @return
     */
    @AutoLog(value = "关键词-通过id删除")
    @Operation(summary = "关键词-通过id删除")
    @PreAuthorize("@jps.requiresPermissions('keywords:seo_keywords:delete')")
    @DeleteMapping(value = "/delete")
    public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
        seoKeywordsService.removeById(id);
        return Result.OK("删除成功!");
    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @AutoLog(value = "关键词-批量删除")
    @Operation(summary = "关键词-批量删除")
    @PreAuthorize("@jps.requiresPermissions('keywords:seo_keywords:deleteBatch')")
    @DeleteMapping(value = "/deleteBatch")
    public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
        this.seoKeywordsService.removeByIds(Arrays.asList(ids.split(",")));
        return Result.OK("批量删除成功!");
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    // @AutoLog(value = "关键词-通过id查询")
    @Operation(summary = "关键词-通过id查询")
    @GetMapping(value = "/queryById")
    public Result<SeoKeywords> queryById(@RequestParam(name = "id", required = true) String id) {
        SeoKeywords seoKeywords = seoKeywordsService.getById(id);
        if (seoKeywords == null) {
            return Result.error("未找到对应数据");
        }
        return Result.OK(seoKeywords);
    }

    /**
     * 导出excel
     *
     * @param request
     */
    @PreAuthorize("@jps.requiresPermissions('keywords:seo_keywords:exportXls')")
    @RequestMapping(value = "/exportXls")
    public ModelAndView exportXls(HttpServletRequest request, SeoKeywords seoKeywords) {
        return super.exportXls(request, seoKeywords, SeoKeywords.class, "关键词");
    }

    /** 关键词排名页面 - 列表数据 */
    @AutoLog(value = "营销推广-SEO-关键词排名统计查询接口")
    @RequestMapping(value = "/keywordList")
    public Result<?> queryRankInfoList(
            AvesApiSearchKeywordsDTO avesApiSearchKeywordsDTO,
            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
            HttpServletRequest req)
            throws Exception {
        //        if (sysBaseAPI.isNotOwnSite(avesApiSearchKeywordsDTO.getSiteCode())) {
        //            return Result.OK(new Page<>());
        //        }
        // 参数判断
        IPage<SeoKeywords> pageList = new Page<>();
        String siteCode = req.getParameter("siteCode");
        if (StringUtil.isEmpty(siteCode)) {
            siteCode = avesApiSearchKeywordsDTO.getSiteCode();
        }
        if (StringUtil.isEmpty(siteCode)) {
            log.info("缺少siteCode参数");
            return Result.OK(pageList);
        }
        String subscriptionId = avesApiSearchKeywordsDTO.getSubscriptionId();
        Integer keywordType = avesApiSearchKeywordsDTO.getKeywordType();
        // 查询关键词
        String column = req.getParameter("column");
        String order = req.getParameter("order");
        String buttonColumn = req.getParameter("buttonColmn");
        String buttonSort = req.getParameter("buttonSort");
        Page<SeoKeywords> page = new Page<SeoKeywords>(pageNo, pageSize);
        List<String> dateList =
                seoKeywordsService.getKeywordsLastSevenDays(siteCode, keywordType, subscriptionId);
        pageList =
                seoKeywordsService.getKeywordList(
                        page,
                        avesApiSearchKeywordsDTO.getRankStart(),
                        avesApiSearchKeywordsDTO.getRankEnd(),
                        avesApiSearchKeywordsDTO.getKeywords(),
                        siteCode,
                        keywordType,
                        avesApiSearchKeywordsDTO.getReachStandard() != null
                                && avesApiSearchKeywordsDTO.getReachStandard() == 1,
                        subscriptionId,
                        column,
                        order,
                        buttonColumn,
                        buttonSort,
                        dateList);

        // 给关键词对象增加排名信息
        if (pageList == null) {
            return Result.error("未获取到关键词数据");
        }
        seoKeywordsRankService.addRankInfoByIntervalTimeByType(
                pageList.getRecords(),
                avesApiSearchKeywordsDTO.getKeywordType(),
                siteCode,
                subscriptionId,
                dateList);
        log.info(pageList.toString());
        return Result.OK(pageList);
    }

    /**
     * Excel导出 关键词排名的导出
     *
     * @param avesApiSearchKeywordsDTO 查询参数
     * @return 询盘列表
     */
    @RequestMapping(value = "/exportExcel")
    public void exportExcel(
            AvesApiSearchKeywordsDTO avesApiSearchKeywordsDTO,
            HttpServletRequest req,
            HttpServletResponse response)
            throws Exception {
        Result<?> res = this.queryRankInfoList(avesApiSearchKeywordsDTO, 1, 5000, req);
        if (res == null || res.getResult() == null) {
            return;
        }
        IPage<SeoKeywords> pageList = (IPage<SeoKeywords>) res.getResult();
        if (pageList.getRecords().isEmpty()) {
            return;
        }
        ArrayList<String> columns = null;
        // 表头信息
        if (sysAdwebApi.isAdmin()) {
            columns = new ArrayList<>(Arrays.asList("序号", "关键词", "url", "关键词类型"));
        } else {
            columns = new ArrayList<>(Arrays.asList("序号", "关键词", "关键词类型"));
        }

        // 将关键词信息改为可以导出的格式
        List<Map<String, Object>> list = new ArrayList<>();
        List<SeoKeywords> enquiryList = pageList.getRecords();

        List<String> dates = pageList.getRecords().get(0).getDateList();
        columns.addAll(dates);

        boolean isAdmin = sysAdwebApi.isAdmin();

        for (int i = 0; i < enquiryList.size(); i++) {
            SeoKeywords seoKeywords = enquiryList.get(i);
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("序号", i + 1);
            map.put("关键词", seoKeywords.getKeywords());
            if (isAdmin) {
                map.put("url", seoKeywords.getPositionUrl());
            }
            if (seoKeywords.getKeywordType() == 1) {
                map.put("关键词类型", "指定词");
            } else if (seoKeywords.getKeywordType() == 2) {
                map.put("关键词类型", "长尾词");
            }
            // 遍历日期
            for (String date : dates) {
                map.put(
                        date,
                        Integer.valueOf(
                                seoKeywords.getRankInfo().get(date) == null
                                        ? "0"
                                        : seoKeywords.getRankInfo().get(date)));
            }
            list.add(map);
        }
        StringBuilder fileName = new StringBuilder();
        fileName.append("关键词排名导出");

        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        // test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
        ServletOutputStream out = null;
        ExcelWriter writer = ExcelUtil.getWriter(true);
        try {
            Sheet sheet = writer.getSheet();
            sheet.setDefaultColumnWidth(12);
            sheet.setDefaultRowHeightInPoints(20);
            // 设置请求头属性
            response.setHeader(
                    "Content-Disposition",
                    "attachment;filename="
                            + new String(
                                    (fileName + ".xlsx").getBytes(), StandardCharsets.ISO_8859_1));
            out = response.getOutputStream();
            log.info("导出数据：" + list);
            writer.write(list, true);
            seoKeywordsRankService.setSizeColumn(sheet, list.get(0).size());
            // 写出到文件
            writer.flush(out, true);
            // 关闭writer，释放内存
            writer.close();
            // 此处记得关闭输出Servlet流
            IoUtil.close(out);
        } catch (IOException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 通过excel导入数据
     *
     * @param request
     * @return
     */
    @PreAuthorize("@jps.requiresPermissions('keywords:seo_keywords:importExcel')")
    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public Result<?> importExcel(HttpServletRequest request) {
        String siteCode = request.getParameter("siteCode");
        log.info("siteCode：" + siteCode);
        if (StringUtil.isEmpty(siteCode)) {
            return Result.error("站点code为空！");
        }

        AdwebSite site = adwebSiteService.getSiteByCode(siteCode);
        if (site == null) {
            return Result.error("站点不存在！");
        }
        if (StringUtil.isEmpty(site.getDomain())) {
            return Result.error("站点域名未设置！");
        }
        SeoPlanSubscription currentSeoAndSiteSubscription =
                seoPlanSubscriptionService.getCurrentSeoAndSiteSubscription(site.getId());

        if (currentSeoAndSiteSubscription == null) {
            return Result.error("当前站点没有有效的订阅套餐！");
        }
        Date now = new Date();

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            MultipartFile file = entity.getValue();
            FileInputStream stream = null;
            try {
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMdd");
                String formattedDate = dateFormat.format(now);
                File realFile =
                        new File(
                                uploadUrl,
                                formattedDate + RandomStringUtils.random(4, true, true) + ".xlsx");
                file.transferTo(realFile);
                stream = new FileInputStream(realFile);
                XSSFWorkbook workbook = new XSSFWorkbook(stream);
                XSSFSheet sheet = workbook.getSheetAt(0);
                int last = sheet.getLastRowNum();

                List<SeoKeywords> list = new ArrayList<>();
                for (int i = 1; i <= last; i++) {
                    XSSFRow row = sheet.getRow(i);

                    XSSFCell cell1 = row.getCell(0);
                    if (cell1 == null || cell1.getCellTypeEnum() == CellType.BLANK) {
                        continue;
                    }
                    cell1.setCellType(CellType.STRING);
                    String value1 = cell1.getStringCellValue();
                    value1 = value1.trim();
                    log.info(value1 + " ");

                    XSSFCell cell2 = row.getCell(1);
                    if (cell2 == null || cell2.getCellTypeEnum() == CellType.BLANK) {
                        continue;
                    }
                    cell2.setCellType(CellType.STRING);
                    String value2 = cell2.getStringCellValue();
                    value2 = value2.trim();
                    value2 = value2.toLowerCase();
                    log.info(value2 + " ");
                    if (StringUtil.isEmpty(value2)) {
                        continue;
                    }
                    SeoKeywords seoKeywords = new SeoKeywords();
                    seoKeywords.setAppKey("adweb");
                    seoKeywords.setSiteCode(site.getCode());
                    seoKeywords.setKeywords(value2);
                    seoKeywords.setDomain(site.getDomain());
                    seoKeywords.setCreateTime(now);
                    seoKeywords.setStatus(1);
                    seoKeywords.setPriority(3);
                    seoKeywords.setOptimizeProcess(0);
                    seoKeywords.setOptimizeStatus(0);
                    if ("指定关键词".equals(value1)) {
                        seoKeywords.setKeywordType(1);
                    } else {
                        seoKeywords.setKeywordType(2);
                    }
                    seoKeywords.setLastRank(0);
                    seoKeywords.setSearchStatus(0);
                    seoKeywords.setSubscriptionId(currentSeoAndSiteSubscription.getId());
                    list.add(seoKeywords);
                }

                // 新增关键词本身去重
                Integer originalSize = list.size();
                List<SeoKeywords> listNew = distinct(list);
                // 去重
                LambdaQueryWrapper<SeoKeywords> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(SeoKeywords::getSiteCode, site.getCode());
                queryWrapper.ne(SeoKeywords::getStatus, 0);
                queryWrapper.eq(
                        SeoKeywords::getSubscriptionId, currentSeoAndSiteSubscription.getId());
                List<SeoKeywords> existingKeywordList = seoKeywordsService.list(queryWrapper);
                if (CollectionUtil.isNotEmpty(existingKeywordList)) {
                    List<String> existingKeywords = new ArrayList<>();
                    for (SeoKeywords keywords : existingKeywordList) {
                        existingKeywords.add(keywords.getKeywords());
                    }
                    for (int i = listNew.size() - 1; i >= 0; i--) {
                        SeoKeywords word = listNew.get(i);
                        if (existingKeywords.contains(word.getKeywords())) {
                            listNew.remove(i);
                        }
                    }
                }

                if (CollectionUtil.isEmpty(listNew)) {
                    return Result.OK("关键词导入个数为0！");
                }

                String msg = "关键词导入成功！关键词个数：" + listNew.size();
                if (originalSize > listNew.size()) {
                    msg =
                            "关键词导入成功！关键词个数："
                                    + listNew.size()
                                    + "，重复关键词已去除！重复个数："
                                    + (originalSize - listNew.size());
                }

                return seoKeywordsService.saveBatch(listNew)
                        ? Result.OK(msg)
                        : Result.error("关键词导入失败！");

            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return Result.error("文件导入失败:" + e.getMessage());
            } finally {
                try {
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return Result.error("文件导入失败！");
    }

    /**
     * @param seoKeywords
     * @return
     */
    @GetMapping(value = "/getSeoKeyword")
    public Result<?> getSeoKeyword(SeoKeywords seoKeywords) {
        LambdaQueryWrapper<SeoKeywords> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SeoKeywords::getKeywords, seoKeywords.getKeywords());
        queryWrapper.eq(SeoKeywords::getSiteCode, seoKeywords.getSiteCode());
        queryWrapper.ne(SeoKeywords::getStatus, 0);
        queryWrapper.eq(SeoKeywords::getSubscriptionId, seoKeywords.getSubscriptionId());
        List<SeoKeywords> seoKeywordsList = seoKeywordsService.list(queryWrapper);
        if (!seoKeywordsList.isEmpty()) {
            return Result.OK(false);
        }
        return Result.OK(true);
    }

    /**
     * 关键词类型更改
     *
     * @return
     */
    @PostMapping(value = "/changeType")
    public Result<?> changeType(@RequestBody ChangeTypeDTO changeTypeDTO) {
        Integer id = changeTypeDTO.getId();
        Integer type = changeTypeDTO.getType();
        if (id == null || type == null) {
            return Result.error("参数错误！");
        }
        SeoKeywords temp = seoKeywordsService.getById(id);
        // 改为指定关键词时，判断是否是别的关键词的相关关键词，改为长尾关键词时，判断是否具有相关关键词
        if (type == 2) {
            LambdaQueryWrapper<SeoKeywords> queryWrapper2 = new LambdaQueryWrapper<>();
            queryWrapper2.ne(SeoKeywords::getStatus, 0);
            queryWrapper2.eq(SeoKeywords::getSubscriptionId, temp.getSubscriptionId());
            queryWrapper2.eq(SeoKeywords::getRelatedKeywordId, id);
            if (seoKeywordsService.count(queryWrapper2) > 0) {
                return Result.error("此关键词已经关联长尾词！");
            }
        }
        if (type == 1) {
            SeoKeywords word = seoKeywordsService.getById(id);
            if (word != null && word.getRelatedKeywordId() != null) {
                return Result.error("此关键词已关联指定关键词！");
            }
        }

        SeoKeywords seoKeywords = new SeoKeywords();
        seoKeywords.setId(id);
        seoKeywords.setKeywordType(type);
        boolean flag = seoKeywordsService.updateById(seoKeywords);
        if (flag) {
            return Result.OK("关键词类型更改成功！");
        }
        return Result.error("更改失败！");
    }
}
