package com.gxwebsoft.cms.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.cms.entity.*;
import com.gxwebsoft.cms.mapper.*;
import com.gxwebsoft.cms.param.*;
import com.gxwebsoft.cms.service.*;
import com.gxwebsoft.common.core.utils.JSONUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.Company;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.service.CompanyService;
import com.gxwebsoft.common.system.service.UserService;
import com.gxwebsoft.project.entity.Project;
import com.gxwebsoft.project.service.ProjectService;
import com.gxwebsoft.shop.vo.ShopVo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 网站信息记录表Service实现
 *
 * @author 科技小王子
 * @since 2024-09-10 20:36:14
 */
@Slf4j
@Service
public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebsite> implements CmsWebsiteService {

    private static final String SITE_INFO_KEY_PREFIX = "SiteInfo:";
    @Resource
    private CmsWebsiteFieldMapper cmsWebsiteFieldMapper;
    @Resource
    private CmsModelMapper cmsModelMapper;
    @Resource
    private CmsNavigationMapper cmsNavigationMapper;
    @Resource
    private CmsLangLogMapper cmsLangLogMapper;
    @Resource
    private CmsAdMapper cmsAdMapper;
    @Resource
    private CmsLinkMapper cmsLinkMapper;
    @Resource
    private CmsArticleMapper cmsArticleMapper;
    @Resource
    private CmsWebsiteFieldService cmsWebsiteFieldService;
    @Resource
    private CmsModelService cmsModelService;
    @Resource
    private CmsNavigationService cmsNavigationService;
    @Resource
    private CmsLangLogService cmsLangLogService;
    @Resource
    private CmsAdService cmsAdService;
    @Resource
    private CmsLinkService cmsLinkService;
    @Resource
    private CmsArticleService cmsArticleService;
    @Resource
    private CmsArticleContentService cmsArticleContentService;
    @Resource
    private CmsWebsiteMapper cmsWebsiteMapper;
    @Resource
    private ProjectService projectService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private UserService userService;
    @Resource
    private CompanyService companyService;

    @Override
    public PageResult<CmsWebsite> pageRel(CmsWebsiteParam param) {
        PageParam<CmsWebsite, CmsWebsiteParam> page = new PageParam<>(param);
        page.setDefaultOrder("create_time desc");
        List<CmsWebsite> list = baseMapper.selectPageRel(page, param);
        list.forEach(d -> {
            LocalDateTime now = LocalDateTime.now();
            if (d.getExpirationTime() != null) {
                // 将Date转换为LocalDateTime进行计算
                LocalDateTime expirationTime = d.getExpirationTime().toInstant()
                        .atZone(java.time.ZoneId.systemDefault())
                        .toLocalDateTime();
                // 即将过期(30天内过期的)
                d.setSoon(expirationTime.minusDays(30).compareTo(now));
                // 是否过期 -1已过期 大于0 未过期
                d.setStatus(expirationTime.compareTo(now));
            } else {
                d.setSoon(0);
                d.setStatus(1);
            }
        });
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public List<CmsWebsite> listRel(CmsWebsiteParam param) {
        List<CmsWebsite> list = baseMapper.selectListRel(param);
        // 排序
        PageParam<CmsWebsite, CmsWebsiteParam> page = new PageParam<>();
        page.setDefaultOrder("create_time desc");
        return page.sortRecords(list);
    }

    @Override
    public CmsWebsite getByIdRel(Integer websiteId) {
        CmsWebsiteParam param = new CmsWebsiteParam();
        param.setWebsiteId(websiteId);
        return param.getOne(baseMapper.selectListRel(param));
    }

  @Override
  public PageResult<CmsWebsite> pageRelAll(CmsWebsiteParam param) {
    PageParam<CmsWebsite, CmsWebsiteParam> page = new PageParam<>(param);
    page.setDefaultOrder("create_time desc");
    List<CmsWebsite> list = baseMapper.selectPageRelAll(page, param);
    list.forEach(d -> {
        LocalDateTime now = LocalDateTime.now();
        if (d.getExpirationTime() != null) {
            // 将Date转换为LocalDateTime进行计算
            LocalDateTime expirationTime = d.getExpirationTime().toInstant()
                    .atZone(java.time.ZoneId.systemDefault())
                    .toLocalDateTime();
            // 即将过期(30天内过期的)
            d.setSoon(expirationTime.minusDays(30).compareTo(now));
            // 是否过期 -1已过期 大于0 未过期
            d.setStatus(expirationTime.compareTo(now));
        } else {
            d.setSoon(0);
            d.setStatus(1);
        }
    });
    return new PageResult<>(list, page.getTotal());
  }

  @Override
  public CmsWebsite create(CmsWebsite website) {
    final User loginUser = website.getLoginUser();
    // 生成全局唯一 websiteCode：若调用方已指定则保留，否则自动生成
    if (StrUtil.isBlank(website.getWebsiteCode())) {
      website.setWebsiteCode(generateUniqueCode("template-" + loginUser.getTenantId()));
    }
    website.setWebsiteIcon("/favicon.ico");
    // 根据应用类型设置管理后台地址和 websiteType 字符串
    // 10=web(Web应用) | 20=miniprogram(小程序) | 30=mobile(移动App) | 40=api(API服务) | 50=internal(内部工具)
    Integer siteType = website.getType();
    if (siteType == null) siteType = 10;
    String adminUrl;
    String websiteTypeName;
    switch (siteType) {
      case 20:
        adminUrl = "https://mp.websoft.top";
        websiteTypeName = "miniprogram";
        break;
      case 30:
        adminUrl = "https://app.websoft.top";
        websiteTypeName = "mobile";
        break;
      case 40:
        adminUrl = "https://api.websoft.top";
        websiteTypeName = "api";
        break;
      case 50:
        adminUrl = "https://oa.websoft.top";
        websiteTypeName = "internal";
        break;
      case 10:
      default:
        adminUrl = "https://site.websoft.top";
        websiteTypeName = "web";
        break;
    }
    website.setAdminUrl(adminUrl);
    // 若前端已传 websiteType 则优先使用，否则用 type 推导值
    if (StrUtil.isBlank(website.getWebsiteType())) {
      website.setWebsiteType(websiteTypeName);
    }
    website.setVersion(10);
    website.setExpirationTime(java.util.Date.from(LocalDateTime.now().plusMonths(1)
            .atZone(java.time.ZoneId.systemDefault()).toInstant()));
    website.setUserId(loginUser.getUserId());
    website.setDeveloper(loginUser.getNickname());
    website.setTenantId(loginUser.getTenantId());
    website.setTemplateId(loginUser.getTemplateId());
    website.setCompanyId(loginUser.getCompanyId());

    // 默认运行中：status（后台枚举）与 running（getSiteInfo 状态展示用）保持一致，
    // 避免公开站读取到空值显示「状态未知」；调用方可覆盖
    if (website.getStatus() == null) {
      website.setStatus(1);
    }
    if (website.getRunning() == null) {
      website.setRunning(1);
    }

    // 初始化数据
    if(save(website)){
      // 同步域名到 company.app_domain（websopy 租户表），保证 cms_website.domain 与 app_domain 一致
      if (StrUtil.isNotBlank(website.getDomain())) {
        try {
          Company company = companyService.getByTenantIdRel(loginUser.getTenantId());
          if (company != null) {
            // 默认域名由前端带上 https:// 前缀，自定义域名已去除前缀，此处原样保存
            company.setDomain(website.getDomain());
            companyService.updateById(company);
          }
        } catch (Exception e) {
          log.warn("同步域名到 company 失败, tenantId={}", loginUser.getTenantId(), e);
        }
      }

      // 插入网站设置记录
//      final CmsWebsiteSetting setting = new CmsWebsiteSetting();
//      setting.setWebsiteId(website.getWebsiteId());
//      setting.setCreateTime(DateUtil.date());
//      setting.setUpdateTime(DateUtil.date());
//      cmsWebsiteSettingService.save(setting);

      // 将网站创建者的userId做为查询条件 10257(4716),10324(6978),10398(26564)
      Integer websiteUserId = website.getTemplateId();

      // 只有当templateId存在时才执行复制操作
      if (websiteUserId != null && websiteUserId > 0) {
        // TODO 国际化
        final CmsLangLogParam cmsLangLogParam = new CmsLangLogParam();
        cmsLangLogParam.setWebsiteUserId(websiteUserId);
        final List<CmsLangLog> logs = cmsLangLogMapper.selectListAllRel(cmsLangLogParam);
        logs.forEach(d->{
          d.setTenantId(loginUser.getTenantId());
        });
        cmsLangLogService.saveBatch(logs);

        // TODO 复制参数
        final CmsWebsiteFieldParam param = new CmsWebsiteFieldParam();
        param.setUserId(websiteUserId);
        final List<CmsWebsiteField> fields = cmsWebsiteFieldMapper.selectListAllRel(param);
        fields.forEach(d->{
          d.setTenantId(loginUser.getTenantId());
        });
        cmsWebsiteFieldService.saveBatch(fields);

        // TODO 复制模型
        final CmsModelParam modelParam = new CmsModelParam();
        modelParam.setWebsiteUserId(websiteUserId);
        final List<CmsModel> models = cmsModelMapper.selectListAllRel(modelParam);
        models.forEach(d->{
          d.setUserId(loginUser.getUserId());
          d.setTenantId(loginUser.getTenantId());
        });
        cmsModelService.saveBatch(models);

        // TODO 复制广告
        final CmsAdParam cmsAdParam = new CmsAdParam();
        cmsAdParam.setWebsiteUserId(websiteUserId);
        final List<CmsAd> ads = cmsAdMapper.selectListAllRel(cmsAdParam);
        ads.forEach(d -> {
          d.setUserId(loginUser.getUserId());
          d.setTenantId(loginUser.getTenantId());
        });
        cmsAdService.saveBatch(ads);

        // TODO 复制链接
        CmsLinkParam cmsLinkParam = new CmsLinkParam();
        cmsLinkParam.setWebsiteUserId(websiteUserId);
        final List<CmsLink> links = cmsLinkMapper.selectListAllRel(cmsLinkParam);
        links.forEach(d -> {
          d.setUserId(loginUser.getUserId());
          d.setTenantId(loginUser.getTenantId());
        });
        cmsLinkService.saveBatch(links);

        // TODO 复制栏目和文章、文章内容
        CmsNavigationParam cmsNavigationParam = new CmsNavigationParam();
        cmsNavigationParam.setWebsiteUserId(websiteUserId);
        cmsNavigationParam.setParentId(0);
        final List<CmsNavigation> parents = cmsNavigationMapper.selectListAllRel(cmsNavigationParam);
        parents.forEach(d -> {
          Integer navigationId = d.getNavigationId();
          // 复制顶级栏目
          d.setTenantId(loginUser.getTenantId());
          d.setUserId(loginUser.getUserId());
          if (cmsNavigationService.save(d)) {
            cmsNavigationService.saveAsync(d);
            // 复制栏目文章
            CmsArticleParam cmsArticleParam = new CmsArticleParam();
            cmsArticleParam.setWebsiteUserId(websiteUserId);
            cmsArticleParam.setCategoryId(navigationId);
            final List<CmsArticle> articles = cmsArticleMapper.selectListAllRel(cmsArticleParam);
            articles.forEach(a -> {
              a.setCategoryId(d.getNavigationId());
              a.setUserId(loginUser.getUserId());
              a.setTenantId(loginUser.getTenantId());
              if (cmsArticleService.save(a)) {
                final CmsArticleContent content = new CmsArticleContent();
                content.setArticleId(a.getArticleId());
                content.setContent(a.getContent());
                cmsArticleContentService.save(content);
              }
            });
            // 复制子栏目
            cmsNavigationParam.setParentId(navigationId);
            final List<CmsNavigation> navigations = cmsNavigationMapper.selectListAllRel(cmsNavigationParam);
                navigations.forEach(c -> {
                  cmsArticleParam.setCategoryId(c.getNavigationId());
                  c.setParentId(d.getNavigationId());
                  c.setTenantId(loginUser.getTenantId());
                  c.setUserId(loginUser.getUserId());
                  cmsNavigationService.save(c);
                  cmsNavigationService.saveAsync(c);
                  // 复制子栏目文章
                  final List<CmsArticle> articles2 = cmsArticleMapper.selectListAllRel(cmsArticleParam);
                  articles2.forEach(a2 -> {
                    a2.setCategoryId(c.getNavigationId());
                    a2.setParentId(c.getParentId());
                    a2.setUserId(loginUser.getUserId());
                    a2.setTenantId(loginUser.getTenantId());
                    if (cmsArticleService.save(a2)) {
                      final CmsArticleContent content = new CmsArticleContent();
                      content.setArticleId(a2.getArticleId());
                      content.setContent(a2.getContent());
                      cmsArticleContentService.save(content);
                    }
                  });
                });
          }
        });
      } else {
        log.warn("没有有效的模板ID，跳过复制操作");
      }

      // 新增项目
      final Project project = new Project();
      project.setUserId(website.getUserId());
      project.setAppName(website.getWebsiteName());
      project.setAppIcon(website.getWebsiteIcon());
      project.setAppCode(website.getWebsiteCode());
      project.setAdminUrl(website.getAdminUrl());
      project.setRenewMoney(website.getPrice());
      project.setWebsiteId(website.getWebsiteId());
      project.setAdminUrl(website.getAdminUrl());
      project.setAppType(website.getWebsiteType());
      project.setAppIcon(website.getWebsiteLogo());
      project.setAppUrl(website.getDomain());
      project.setCompanyId(website.getUserId());
      project.setTenantId(5);
      projectService.save(project);
    }
    return website;
  }

  @Override
  public CmsWebsite getByIdRelAll(Integer id) {
    return cmsWebsiteMapper.getByIdRelAll(id);
  }

  @Override
  public boolean updateByIdAll(CmsWebsite cmsWebsite) {
    return baseMapper.updateByIdAll(cmsWebsite);
  }

  @Override
  public boolean removeByIdAll(Integer id) {
    return baseMapper.removeByIdAll(id);
  }

  @Override
  public CmsWebsite getByTenantId(Integer tenantId) {
    return baseMapper.getByTenantId(tenantId);
  }

  @Override
  public ShopVo getSiteInfo(Integer tenantId) {
    // 参数验证
    if (ObjectUtil.isEmpty(tenantId)) {
      throw new IllegalArgumentException("租户ID不能为空");
    }

    // 尝试从缓存获取
    String cacheKey = SITE_INFO_KEY_PREFIX + tenantId;
    String siteInfo = redisUtil.get(cacheKey);
    if (StrUtil.isNotBlank(siteInfo)) {
      log.info("从缓存获取网站信息，租户ID: {}", tenantId);
      try {
        // 兼容历史缓存：JSON "null" 会被解析为 null；此时应视为未命中并回源数据库。
        ShopVo cacheVo = JSONUtil.parseObject(siteInfo, ShopVo.class);
        if (cacheVo != null) {
          return cacheVo;
        }
        log.warn("网站信息缓存命中但内容为空(null)，清理缓存后回源数据库，租户ID: {}", tenantId);
        redisUtil.delete(cacheKey);
      } catch (Exception e) {
        log.warn("缓存解析失败，清理缓存后从数据库重新获取: {}", e.getMessage());
        redisUtil.delete(cacheKey);
      }
    }

    // 从数据库获取站点信息
    CmsWebsite website = getWebsiteFromDatabase(tenantId);


    if (website == null) {
      throw new RuntimeException("请先创建站点");
    }

    // 构建完整的网站信息
    buildCompleteWebsiteInfo(website);

    // 处理过期时间
    CmsWebsiteServiceImplHelper.processExpirationTime(website);

    // 转换为VO对象
    ShopVo websiteVO = CmsWebsiteServiceImplHelper.convertToVO(website);

    // 缓存结果
    try {
      redisUtil.set(cacheKey, websiteVO, 1L, TimeUnit.DAYS);
    } catch (Exception e) {
      log.warn("缓存网站信息失败: {}", e.getMessage());
    }

    log.info("获取网站信息成功，网站ID: {}, 租户ID: {}", website.getWebsiteId(), tenantId);
    return websiteVO;
  }

  @Override
  public void clearSiteInfoCache(Integer tenantId) {
    if (tenantId != null) {
      String cacheKey = SITE_INFO_KEY_PREFIX + tenantId;
      redisUtil.delete(cacheKey);
      log.info("清除网站信息缓存成功，租户ID: {}", tenantId);
    }
  }

  // ─── 发布管理 ──────────────────────────────────────────────────────────

  @Override
  public void submitReview(Integer websiteId, String priceType, Integer price,
                           String subscriptionPeriod, String appDescription,
                           String detailDescription, String screenshots, Integer userId) {
    CmsWebsite website = getById(websiteId);
    if (website == null) throw new RuntimeException("应用不存在");
    // 只有开发中 / 审核未通过 才能提交
    String cur = website.getPublishStatus();
    if (!"developing".equals(cur) && !"rejected".equals(cur) && cur != null) {
      throw new RuntimeException("当前状态不允许提交审核");
    }
    CmsWebsite update = new CmsWebsite();
    update.setWebsiteId(websiteId);
    update.setPublishStatus("pending_review");
    update.setPriceType(priceType != null ? priceType : "free");
    update.setPrice(price != null ? new java.math.BigDecimal(price).movePointLeft(2) : java.math.BigDecimal.ZERO);
    update.setSubscriptionPeriod(subscriptionPeriod);
    update.setAppDescription(appDescription);
    update.setDetailDescription(detailDescription);
    update.setScreenshots(screenshots);
    update.setPublishApplyTime(new java.util.Date());
    update.setRejectReason(null);
    updateById(update);
  }

  @Override
  public void withdrawReview(Integer websiteId, Integer userId) {
    CmsWebsite website = getById(websiteId);
    if (website == null) throw new RuntimeException("应用不存在");
    if (!"pending_review".equals(website.getPublishStatus())) {
      throw new RuntimeException("当前状态不是待审核，无法撤回");
    }
    CmsWebsite update = new CmsWebsite();
    update.setWebsiteId(websiteId);
    update.setPublishStatus("developing");
    updateById(update);
  }

  @Override
  public void unpublish(Integer websiteId, Integer userId) {
    CmsWebsite website = getById(websiteId);
    if (website == null) throw new RuntimeException("应用不存在");
    if (!"published".equals(website.getPublishStatus())) {
      throw new RuntimeException("应用未上架，无法下架");
    }
    CmsWebsite update = new CmsWebsite();
    update.setWebsiteId(websiteId);
    update.setPublishStatus("deprecated");
    updateById(update);
  }

  @Override
  public void approveReview(Integer websiteId, Integer reviewerId) {
    CmsWebsite website = getById(websiteId);
    if (website == null) throw new RuntimeException("应用不存在");
    if (!"pending_review".equals(website.getPublishStatus())) {
      throw new RuntimeException("当前状态不是待审核");
    }
    CmsWebsite update = new CmsWebsite();
    update.setWebsiteId(websiteId);
    update.setPublishStatus("published");
    update.setMarket(true);
    update.setReviewerId(reviewerId);
    update.setReviewTime(new java.util.Date());
    update.setPublishTime(new java.util.Date());
    update.setRejectReason(null);
    updateById(update);
  }

  @Override
  public void rejectReview(Integer websiteId, String rejectReason, Integer reviewerId) {
    CmsWebsite website = getById(websiteId);
    if (website == null) throw new RuntimeException("应用不存在");
    if (!"pending_review".equals(website.getPublishStatus())) {
      throw new RuntimeException("当前状态不是待审核");
    }
    CmsWebsite update = new CmsWebsite();
    update.setWebsiteId(websiteId);
    update.setPublishStatus("rejected");
    update.setRejectReason(rejectReason);
    update.setReviewerId(reviewerId);
    update.setReviewTime(new java.util.Date());
    updateById(update);
  }

  @Override
  public PageResult<CmsWebsite> pageReviews(CmsWebsiteParam param) {
    com.baomidou.mybatisplus.extension.plugins.pagination.Page<CmsWebsite> page =
        new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getPage() != null ? param.getPage() : 1, param.getLimit() != null && param.getLimit() > 0 ? param.getLimit() : 20);
    com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CmsWebsite> wrapper =
        new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CmsWebsite>()
            .eq(CmsWebsite::getDeleted, 0)
            .isNotNull(CmsWebsite::getPublishStatus)
            .ne(CmsWebsite::getPublishStatus, "developing")
            .eq(cn.hutool.core.util.ObjectUtil.isNotEmpty(param.getPublishStatus()),
                CmsWebsite::getPublishStatus, param.getPublishStatus())
            .and(cn.hutool.core.util.ObjectUtil.isNotEmpty(param.getKeywords()), q ->
                q.like(CmsWebsite::getWebsiteName, param.getKeywords())
                 .or().like(CmsWebsite::getWebsiteCode, param.getKeywords()))
            .last("ORDER BY FIELD(publish_status,'pending_review','rejected','published','deprecated'), publish_apply_time DESC");
    baseMapper.selectPage(page, wrapper);
    return new com.gxwebsoft.common.core.web.PageResult<>(page.getRecords(), page.getTotal());
  }


  private CmsWebsite getWebsiteFromDatabase(Integer tenantId) {
    return getByTenantId(tenantId);
  }

  /**
   * 构建完整的网站信息
   */
  private void buildCompleteWebsiteInfo(CmsWebsite website) {
    // 设置网站状态
    CmsWebsiteServiceImplHelper.setWebsiteStatus(website);

    // 设置网站配置
    CmsWebsiteServiceImplHelper.setWebsiteConfig(website);

    // 设置网站导航
    setWebsiteNavigation(website);

    // 设置网站设置信息
    CmsWebsiteServiceImplHelper.setWebsiteSetting(website);

    // 设置服务器时间信息
    CmsWebsiteServiceImplHelper.setServerTimeInfo(website);
  }

  /**
   * 设置网站导航
   */
  private void setWebsiteNavigation(CmsWebsite website) {
    // 获取顶部导航
    CmsNavigationParam navigationParam = new CmsNavigationParam();
    navigationParam.setHide(0);
    navigationParam.setTop(0);
    navigationParam.setBottom(null);
    List<CmsNavigation> topNavs = cmsNavigationService.listRel(navigationParam);
    website.setTopNavs(topNavs);

    // 获取底部导航
    navigationParam.setTop(null);
    navigationParam.setBottom(0);
    List<CmsNavigation> bottomNavs = cmsNavigationService.listRel(navigationParam);
    website.setBottomNavs(bottomNavs);
  }

  /**
   * 生成全局唯一的 websiteCode。
   * <p>以 baseCode 为基础，若已存在则追加 -2、-3 … 直到找到空闲值。</p>
   *
   * @param baseCode 期望的 code 前缀，如 "site-10398"
   * @return 全局唯一的 websiteCode
   */
  private String generateUniqueCode(String baseCode) {
    String candidate = baseCode;
    int suffix = 2;
    while (count(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CmsWebsite>()
        .eq(CmsWebsite::getWebsiteCode, candidate)) > 0) {
      candidate = baseCode + "-" + suffix;
      suffix++;
    }
    return candidate;
  }
}
