package com.ruoyi.web.modules.trading.support;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.trading.domain.BizEntryIsolation;
import com.ruoyi.web.modules.trading.domain.BizEntryIsolationAlloc;
import com.ruoyi.web.modules.trading.domain.BizIsolationPen;
import com.ruoyi.web.modules.trading.domain.BizSupplier;
import com.ruoyi.web.modules.trading.mapper.BizIsolationPenMapper;
import com.ruoyi.web.modules.trading.mapper.BizSupplierMapper;

/**
 * 入场隔离登记入参校验与 R1～R5 一致性。
 */
@Component
public class EntryIsolationValidation
{
    private static final String DATE_PATTERN = "yyyy-MM-dd";

    @Autowired
    private BizSupplierMapper bizSupplierMapper;

    @Autowired
    private BizIsolationPenMapper bizIsolationPenMapper;

    public void validateListQuery(BizEntryIsolation query)
    {
        if (query == null)
        {
            return;
        }
        validateDateRange(query.getEntryDateStart(), query.getEntryDateEnd());
    }

    /**
     * 规范化主表耳标、分圈耳标与数量，并校验 R1～R5 及外键。
     */
    public void prepareAndValidateForSave(BizEntryIsolation row)
    {
        if (row == null)
        {
            throw new ServiceException("登记数据不能为空");
        }
        trimFields(row);
        if (row.getEntryTime() == null)
        {
            throw new ServiceException("进场时间不能为空");
        }
        if (row.getSupplierId() == null)
        {
            throw new ServiceException("请选择供应商");
        }
        assertSupplierActive(row.getSupplierId());
        validateCertFile(row.getCertFilePath());
        validateCertNo(row.getCertNo());
        validateIsolationDays(row.getIsolationDays());
        List<String> mainTags = EarTagCodec.resolveMainTags(row.getEarTags(), row.getEarTagList());
        if (mainTags.isEmpty())
        {
            throw new ServiceException("请填写耳标号");
        }
        row.setEarTagList(mainTags);
        row.setEarTags(EarTagCodec.encode(mainTags));
        if (row.getQuantity() == null || row.getQuantity() <= 0)
        {
            throw new ServiceException("数量须为正整数");
        }
        if (!row.getQuantity().equals(mainTags.size()))
        {
            throw new ServiceException("总数量须与耳标个数一致");
        }
        List<BizEntryIsolationAlloc> allocs = row.getPenAllocList();
        if (allocs == null || allocs.isEmpty())
        {
            throw new ServiceException("请至少添加一条分圈明细");
        }
        validatePenAllocations(row.getQuantity(), mainTags, allocs);
        row.setExpectedReleaseDate(ReleaseDateCalculator.calculate(row.getEntryTime(), row.getIsolationDays()));
    }

    private void validatePenAllocations(int totalQty, List<String> mainTags, List<BizEntryIsolationAlloc> allocs)
    {
        Set<String> mainSet = new LinkedHashSet<>(mainTags);
        Set<Long> penIds = new HashSet<>();
        Set<String> allocUnion = new LinkedHashSet<>();
        int sumQty = 0;
        int order = 0;
        for (BizEntryIsolationAlloc alloc : allocs)
        {
            if (alloc.getPenId() == null)
            {
                throw new ServiceException("请选择圈位编号");
            }
            if (!penIds.add(alloc.getPenId()))
            {
                BizIsolationPen pen = bizIsolationPenMapper.selectBizIsolationPenByIdAny(alloc.getPenId());
                String code = pen != null ? pen.getPenCode() : String.valueOf(alloc.getPenId());
                throw new ServiceException("圈位 " + code + " 已添加，不可重复");
            }
            assertPenActive(alloc.getPenId());
            List<String> penTags = EarTagCodec.resolveMainTags(alloc.getEarTags(), alloc.getEarTagList());
            if (penTags.isEmpty())
            {
                throw new ServiceException("请为分圈选择耳标号");
            }
            for (String tag : penTags)
            {
                if (!mainSet.contains(tag))
                {
                    throw new ServiceException("存在未分圈的耳标");
                }
                if (!allocUnion.add(tag))
                {
                    throw new ServiceException("耳标 " + tag + " 已分配至其他圈位");
                }
            }
            alloc.setEarTagList(penTags);
            alloc.setEarTags(EarTagCodec.encode(penTags));
            alloc.setQuantity(penTags.size());
            alloc.setSortOrder(order++);
            sumQty += alloc.getQuantity();
            if (!alloc.getQuantity().equals(penTags.size()))
            {
                throw new ServiceException("分圈数量须与本圈耳标个数一致");
            }
        }
        if (sumQty != totalQty)
        {
            throw new ServiceException("分圈数量之和须等于总数量");
        }
        if (!allocUnion.equals(mainSet))
        {
            throw new ServiceException("存在未分圈的耳标");
        }
    }

    private static void trimFields(BizEntryIsolation row)
    {
        if (row.getCertNo() != null)
        {
            row.setCertNo(row.getCertNo().trim());
        }
        if (row.getCertFileUrl() != null)
        {
            String url = row.getCertFileUrl().trim();
            row.setCertFileUrl(url.isEmpty() ? null : url);
        }
        if (row.getCertFilePath() != null)
        {
            String path = row.getCertFilePath().trim();
            row.setCertFilePath(path.isEmpty() ? null : path);
        }
        if (row.getRemark() != null)
        {
            String remark = row.getRemark().trim();
            row.setRemark(remark.isEmpty() ? null : remark);
        }
    }

    private static void validateCertNo(String certNo)
    {
        if (StringUtils.isEmpty(certNo))
        {
            throw new ServiceException("请填写检疫证号");
        }
        int len = certNo.length();
        if (len < EntryIsolationRules.MIN_CERT_NO_LEN || len > EntryIsolationRules.MAX_CERT_NO_LEN)
        {
            throw new ServiceException("检疫证号长度须在 1～50 字之间");
        }
    }

    private static void validateIsolationDays(Integer days)
    {
        if (days == null || days < EntryIsolationRules.MIN_ISOLATION_DAYS
                || days > EntryIsolationRules.MAX_ISOLATION_DAYS)
        {
            throw new ServiceException("隔离天数须在 1～3650 天之间");
        }
    }

    private static void validateCertFile(String certFilePath)
    {
        if (StringUtils.isEmpty(certFilePath))
        {
            return;
        }
        if (!certFilePath.startsWith("/profile"))
        {
            throw new ServiceException("检疫证文件路径无效");
        }
        if (!EntryIsolationRules.isCertExtensionAllowed(certFilePath))
        {
            throw new ServiceException("仅支持 pdf/doc/docx 等格式，且不超过 30MB");
        }
    }

    private void assertSupplierActive(Long supplierId)
    {
        BizSupplier supplier = bizSupplierMapper.selectBizSupplierById(supplierId);
        if (supplier == null)
        {
            throw new ServiceException("供应商不存在或已删除");
        }
    }

    private void assertPenActive(Long penId)
    {
        BizIsolationPen pen = bizIsolationPenMapper.selectBizIsolationPenById(penId);
        if (pen == null)
        {
            throw new ServiceException("圈位不存在或已删除");
        }
    }

    public static void validateDateRange(String startDate, String endDate)
    {
        boolean hasStart = StringUtils.isNotEmpty(startDate);
        boolean hasEnd = StringUtils.isNotEmpty(endDate);
        if (!hasStart && !hasEnd)
        {
            return;
        }
        Date start = hasStart ? parseQueryDate(startDate, "开始日期") : null;
        Date end = hasEnd ? parseQueryDate(endDate, "结束日期") : null;
        if (start != null && end != null && start.after(end))
        {
            throw new ServiceException("结束日期不能早于开始日期");
        }
    }

    private static Date parseQueryDate(String text, String label)
    {
        try
        {
            SimpleDateFormat fmt = new SimpleDateFormat(DATE_PATTERN);
            fmt.setLenient(false);
            return fmt.parse(text.trim());
        }
        catch (ParseException ex)
        {
            throw new ServiceException(label + "格式无效，应为 yyyy-MM-dd");
        }
    }
}
