package com.ruoyi.generator.service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.SourceProject;
import com.ruoyi.generator.domain.SourcePurchase;
import com.ruoyi.generator.domain.source.SourceProjectResponse;
import com.ruoyi.generator.domain.source.SourcePurchaseResponse;
import com.ruoyi.generator.mapper.SourceProjectMapper;
import com.ruoyi.generator.mapper.SourcePurchaseMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SourceProjectServiceImpl implements ISourceProjectService
{
    private static final Pattern SLUG_PATTERN = Pattern.compile("^[a-z][a-z0-9-]*[a-z0-9]$|^[a-z]$");

    @Autowired
    private SourceProjectMapper sourceProjectMapper;

    @Autowired
    private SourcePurchaseMapper sourcePurchaseMapper;

    @Override
    public SourceProject selectSourceProjectByProjectId(Long projectId)
    {
        return sourceProjectMapper.selectSourceProjectByProjectId(projectId);
    }

    @Override
    public List<SourceProject> selectSourceProjectList(SourceProject sourceProject)
    {
        return sourceProjectMapper.selectSourceProjectList(sourceProject);
    }

    @Override
    public int insertSourceProject(SourceProject sourceProject)
    {
        prepareForSave(sourceProject, true);
        sourceProject.setCreateTime(DateUtils.getNowDate());
        return sourceProjectMapper.insertSourceProject(sourceProject);
    }

    @Override
    public int updateSourceProject(SourceProject sourceProject)
    {
        prepareForSave(sourceProject, false);
        sourceProject.setUpdateTime(DateUtils.getNowDate());
        return sourceProjectMapper.updateSourceProject(sourceProject);
    }

    @Override
    public int deleteSourceProjectByProjectIds(Long[] projectIds)
    {
        return sourceProjectMapper.deleteSourceProjectByProjectIds(projectIds);
    }

    @Override
    public int changeSourceProjectStatus(SourceProject sourceProject)
    {
        sourceProject.setUpdateTime(DateUtils.getNowDate());
        return sourceProjectMapper.changeSourceProjectStatus(sourceProject);
    }

    @Override
    public List<SourceProjectResponse> listPublicProjects(Long userId, SourceProject query)
    {
        List<SourceProjectResponse> responses = new ArrayList<SourceProjectResponse>();
        List<SourceProject> projects = sourceProjectMapper.selectPublicSourceProjectList(query);
        for (SourceProject project : projects)
        {
            responses.add(toProjectResponse(project, hasPaid(userId, project.getProjectId())));
        }
        return responses;
    }

    @Override
    public SourceProjectResponse getPublicProject(Long userId, String slug, boolean increaseView)
    {
        SourceProject project = sourceProjectMapper.selectSourceProjectBySlug(slug);
        assertProjectPublished(project);
        if (increaseView)
        {
            sourceProjectMapper.increaseViewCount(project.getProjectId());
            project.setViewCount(Long.valueOf(defaultLong(project.getViewCount()) + 1L));
        }
        return toProjectResponse(project, hasPaid(userId, project.getProjectId()));
    }

    @Override
    public SourcePurchase purchase(Long userId, Long projectId)
    {
        assertFrontUserLoggedIn(userId);
        SourceProject project = sourceProjectMapper.selectSourceProjectByProjectId(projectId);
        assertProjectPublished(project);
        SourcePurchase paid = sourcePurchaseMapper.selectPaidPurchase(userId, projectId);
        if (paid != null)
        {
            return paid;
        }
        SourcePurchase purchase = new SourcePurchase();
        purchase.setProjectId(projectId);
        purchase.setUserId(userId);
        purchase.setOrderNo(buildOrderNo(userId));
        purchase.setAmount(project.getPrice());
        purchase.setPayStatus("1");
        purchase.setResourceStatus(StringUtils.isNotEmpty(project.getResourceUrl()) ? "1" : "0");
        purchase.setPurchaseTime(DateUtils.getNowDate());
        purchase.setCreateTime(DateUtils.getNowDate());
        sourcePurchaseMapper.insertSourcePurchase(purchase);
        return purchase;
    }

    @Override
    public List<SourcePurchaseResponse> listPurchases(Long userId)
    {
        assertFrontUserLoggedIn(userId);
        List<SourcePurchaseResponse> responses = new ArrayList<SourcePurchaseResponse>();
        List<SourcePurchase> purchases = sourcePurchaseMapper.selectSourcePurchasesByUserId(userId);
        for (SourcePurchase purchase : purchases)
        {
            responses.add(toPurchaseResponse(purchase));
        }
        return responses;
    }

    @Override
    public Map<String, Object> getDownloadInfo(Long userId, Long projectId)
    {
        assertFrontUserLoggedIn(userId);
        SourceProject project = sourceProjectMapper.selectSourceProjectByProjectId(projectId);
        assertProjectPublished(project);
        SourcePurchase paid = sourcePurchaseMapper.selectPaidPurchase(userId, projectId);
        if (paid == null)
        {
            throw new ServiceException("请先购买源码");
        }
        if (StringUtils.isEmpty(project.getResourceUrl()))
        {
            throw new ServiceException("源码资源暂未配置");
        }
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("projectId", project.getProjectId());
        result.put("projectName", project.getProjectName());
        result.put("resourceName", project.getResourceName());
        result.put("resourceUrl", project.getResourceUrl());
        result.put("resourceVersion", project.getResourceVersion());
        return result;
    }

    private SourceProjectResponse toProjectResponse(SourceProject project, boolean purchased)
    {
        SourceProjectResponse response = new SourceProjectResponse();
        response.setProjectId(project.getProjectId());
        response.setSlug(project.getSlug());
        response.setProjectName(project.getProjectName());
        response.setCategory(project.getCategory());
        response.setCoverText(project.getCoverText());
        response.setSummary(project.getSummary());
        response.setDescriptionMd(project.getDescriptionMd());
        response.setPrice(project.getPrice());
        response.setPriceLabel(formatPrice(project.getPrice()));
        response.setViewCount(Long.valueOf(defaultLong(project.getViewCount())));
        response.setUpdateTime(project.getUpdateTime() == null ? project.getCreateTime() : project.getUpdateTime());
        response.setTags(readStringList(project.getTags()));
        response.setModules(readStringList(project.getModules()));
        response.setScenes(readStringList(project.getScenes()));
        response.setPurchased(purchased);
        response.setResourceReady(StringUtils.isNotEmpty(project.getResourceUrl()));
        return response;
    }

    private SourcePurchaseResponse toPurchaseResponse(SourcePurchase purchase)
    {
        SourcePurchaseResponse response = new SourcePurchaseResponse();
        response.setPurchaseId(purchase.getPurchaseId());
        response.setProjectId(purchase.getProjectId());
        response.setProjectName(purchase.getProjectName());
        response.setOrderNo(purchase.getOrderNo());
        response.setAmount(purchase.getAmount());
        response.setAmountLabel(formatPrice(purchase.getAmount()));
        response.setPayStatus(purchase.getPayStatus());
        response.setResourceStatus(purchase.getResourceStatus());
        response.setPurchaseTime(purchase.getPurchaseTime());
        return response;
    }

    private boolean hasPaid(Long userId, Long projectId)
    {
        return userId != null && sourcePurchaseMapper.selectPaidPurchase(userId, projectId) != null;
    }

    private void assertFrontUserLoggedIn(Long userId)
    {
        if (userId == null)
        {
            throw new ServiceException("前台用户未登录");
        }
    }

    private void assertProjectPublished(SourceProject project)
    {
        if (project == null || !"0".equals(project.getStatus()) || "2".equals(project.getDelFlag()))
        {
            throw new ServiceException("源码项目未上架或不存在");
        }
    }

    private void prepareForSave(SourceProject project, boolean inserting)
    {
        if (project == null || StringUtils.isEmpty(project.getProjectName()))
        {
            throw new ServiceException("项目名称不能为空");
        }
        if (StringUtils.isEmpty(project.getSlug()))
        {
            project.setSlug(toSlug(project.getProjectName()));
        }
        if (StringUtils.isEmpty(project.getCoverText()))
        {
            project.setCoverText(project.getSlug().toUpperCase(Locale.ENGLISH));
        }
        project.setTags(normalizeJsonList(project.getTags()));
        project.setModules(normalizeJsonList(project.getModules()));
        project.setScenes(normalizeJsonList(project.getScenes()));
        if (project.getPrice() == null)
        {
            project.setPrice(0L);
        }
        if (project.getViewCount() == null)
        {
            project.setViewCount(0L);
        }
        if (project.getSort() == null)
        {
            project.setSort(0);
        }
        if (StringUtils.isEmpty(project.getStatus()))
        {
            project.setStatus("0");
        }
        if (inserting && StringUtils.isEmpty(project.getDelFlag()))
        {
            project.setDelFlag("0");
        }
    }

    private String toSlug(String value)
    {
        String slug = StringUtils.defaultString(value).trim()
                .replaceAll("[^A-Za-z0-9]+", "-")
                .replaceAll("^-+|-+$", "")
                .replaceAll("-{2,}", "-")
                .toLowerCase(Locale.ENGLISH);
        if (!SLUG_PATTERN.matcher(slug).matches())
        {
            return "source-" + System.currentTimeMillis();
        }
        return slug;
    }

    private List<String> readStringList(String value)
    {
        if (StringUtils.isEmpty(value))
        {
            return new ArrayList<String>();
        }
        try
        {
            List<String> list = JSON.parseArray(value, String.class);
            return list == null ? new ArrayList<String>() : list;
        }
        catch (Exception e)
        {
            return splitList(value);
        }
    }

    private String normalizeJsonList(String value)
    {
        return JSON.toJSONString(readStringList(value));
    }

    private List<String> splitList(String value)
    {
        List<String> list = new ArrayList<String>();
        if (StringUtils.isEmpty(value))
        {
            return list;
        }
        String[] parts = value.split("[,，\\n]");
        for (String part : parts)
        {
            String item = part == null ? "" : part.trim();
            if (StringUtils.isNotEmpty(item))
            {
                list.add(item);
            }
        }
        return list;
    }

    private String formatPrice(Long cents)
    {
        BigDecimal amount = BigDecimal.valueOf(defaultLong(cents), 2).stripTrailingZeros();
        return "¥" + amount.toPlainString();
    }

    private long defaultLong(Long value)
    {
        return value == null ? 0L : value.longValue();
    }

    private String buildOrderNo(Long userId)
    {
        String time = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        String random = UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase(Locale.ENGLISH);
        return "SRC" + time + userId + random;
    }
}
