package com.ruoyi.web.modules.diagnosis.service.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.web.modules.diagnosis.domain.BizMedicalResource;
import com.ruoyi.web.modules.diagnosis.mapper.BizMedicalResourceMapper;
import com.ruoyi.web.modules.diagnosis.service.IBizMedicalResourceService;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceAccountAssigner;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceMultiValueCodec;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourcePublishStatusResolver;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceReferenceChecker;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceRules;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceTypeValidator;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceValidation;
import com.ruoyi.web.modules.industryservice.support.TownDeptSupport;

/**
 * 畜牧医疗资源服务实现。
 */
@Service
public class BizMedicalResourceServiceImpl implements IBizMedicalResourceService
{
    @Autowired
    private BizMedicalResourceMapper bizMedicalResourceMapper;

    @Autowired
    private MedicalResourceReferenceChecker referenceChecker;

    @Autowired
    private MedicalResourceAccountAssigner accountAssigner;

    @Autowired
    private MedicalResourceTypeValidator medicalResourceTypeValidator;

    @Autowired
    private TownDeptSupport townDeptSupport;

    @Override
    public BizMedicalResource selectBizMedicalResourceById(Long id)
    {
        BizMedicalResource row = bizMedicalResourceMapper.selectBizMedicalResourceById(id);
        enrichRow(row);
        return row;
    }

    @Override
    public List<BizMedicalResource> selectBizMedicalResourceList(BizMedicalResource query)
    {
        // 列表入参校验与类型校验须在 Controller 中、startPage() 之前完成（避免 PageHelper 分页错位）
        applyTownDataScope(query);
        List<BizMedicalResource> list = bizMedicalResourceMapper.selectBizMedicalResourceList(query);
        if (list != null)
        {
            for (BizMedicalResource row : list)
            {
                enrichRow(row);
            }
        }
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertBizMedicalResource(BizMedicalResource row)
    {
        medicalResourceTypeValidator.assertAllowed(row.getResourceType());
        MedicalResourceValidation.validateRowForSave(row);
        applyTownDept(row);
        row.setPublishStatus(MedicalResourceRules.PUBLISH_STATUS_DRAFT);
        row.setPublishTime(null);
        row.setOfflineTime(null);
        row.setAccountAssigned(MedicalResourceRules.ACCOUNT_ASSIGNED_NO);
        row.setSysUserId(null);
        row.setAssignedLoginName(null);
        row.setDelFlag(MedicalResourceRules.DEL_FLAG_NORMAL);
        return bizMedicalResourceMapper.insertBizMedicalResource(row);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateBizMedicalResource(BizMedicalResource row)
    {
        if (row.getId() == null)
        {
            throw new ServiceException("主键不能为空");
        }
        BizMedicalResource db = requireRow(row.getId());
        MedicalResourceValidation.validateForUpdate(db);
        MedicalResourceValidation.validateResourceTypeUnchanged(db, row);
        row.setResourceType(db.getResourceType());
        medicalResourceTypeValidator.assertAllowed(row.getResourceType());
        MedicalResourceValidation.validateRowForSave(row);
        applyTownDept(row);

        String oldPhotoPath = db.getPhotoFilePath();
        row.setPublishStatus(db.getPublishStatus());
        row.setPublishTime(db.getPublishTime());
        row.setOfflineTime(db.getOfflineTime());
        row.setAccountAssigned(db.getAccountAssigned());
        row.setSysUserId(db.getSysUserId());
        row.setAssignedLoginName(db.getAssignedLoginName());
        row.setDelFlag(db.getDelFlag());

        int n = bizMedicalResourceMapper.updateBizMedicalResource(row);
        if (n > 0)
        {
            accountAssigner.syncLinkedUserDept(row);
            if (!StringUtils.equals(StringUtils.nvl(oldPhotoPath, ""), StringUtils.nvl(row.getPhotoFilePath(), "")))
            {
                deleteDiskFile(oldPhotoPath);
            }
        }
        return n;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteBizMedicalResourceByIds(Long[] ids, String updateBy)
    {
        if (ids == null || ids.length == 0)
        {
            return 0;
        }
        List<Long> cleaned = new ArrayList<>();
        for (Long id : ids)
        {
            if (id != null)
            {
                cleaned.add(id);
            }
        }
        if (cleaned.isEmpty())
        {
            return 0;
        }
        for (Long id : cleaned)
        {
            BizMedicalResource row = bizMedicalResourceMapper.selectBizMedicalResourceByIdAny(id);
            if (row == null || MedicalResourceRules.DEL_FLAG_DELETED.equals(row.getDelFlag()))
            {
                continue;
            }
            MedicalResourceValidation.validateForDelete(row);
            if (referenceChecker.hasReference(id))
            {
                throw new ServiceException("存在关联业务，无法删除");
            }
            accountAssigner.deleteLinkedAccount(row);
            deleteDiskFile(row.getPhotoFilePath());
        }
        return bizMedicalResourceMapper.logicDeleteBizMedicalResourceByIds(
                cleaned.toArray(new Long[0]), updateBy);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int publish(Long id)
    {
        BizMedicalResource row = requireRow(id);
        MedicalResourceValidation.validateForPublish(row);
        MedicalResourceMultiValueCodec.decodeLists(row);
        MedicalResourceValidation.validateRowForSave(row);
        row.setPublishStatus(MedicalResourceRules.PUBLISH_STATUS_PUBLISHED);
        row.setPublishTime(new Date());
        row.setOfflineTime(null);
        int n = bizMedicalResourceMapper.updateBizMedicalResource(row);
        enrichRow(row);
        return n;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int offline(Long id)
    {
        BizMedicalResource row = requireRow(id);
        MedicalResourceValidation.validateForOffline(row);
        Date publishTime = row.getPublishTime();
        row.setPublishStatus(MedicalResourceRules.PUBLISH_STATUS_DRAFT);
        row.setOfflineTime(new Date());
        row.setPublishTime(publishTime);
        int n = bizMedicalResourceMapper.updateBizMedicalResource(row);
        enrichRow(row);
        return n;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int publishByIds(Long[] ids)
    {
        List<Long> cleaned = cleanIds(ids);
        if (cleaned.isEmpty())
        {
            return 0;
        }
        int count = 0;
        for (Long id : cleaned)
        {
            count += publish(id);
        }
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int offlineByIds(Long[] ids)
    {
        List<Long> cleaned = cleanIds(ids);
        if (cleaned.isEmpty())
        {
            return 0;
        }
        int count = 0;
        for (Long id : cleaned)
        {
            count += offline(id);
        }
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int assignAccount(Long id, String operator)
    {
        return doAssignAccount(requireRow(id), operator);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int assignAccountByIds(Long[] ids, String operator)
    {
        List<Long> cleaned = cleanIds(ids);
        if (cleaned.isEmpty())
        {
            return 0;
        }
        int count = 0;
        for (Long id : cleaned)
        {
            BizMedicalResource row = bizMedicalResourceMapper.selectBizMedicalResourceById(id);
            if (row == null)
            {
                throw new ServiceException("记录不存在或已删除");
            }
            if (row.getAccountAssigned() != null
                    && row.getAccountAssigned() == MedicalResourceRules.ACCOUNT_ASSIGNED_YES)
            {
                continue;
            }
            count += doAssignAccount(row, operator);
        }
        return count;
    }

    private int doAssignAccount(BizMedicalResource row, String operator)
    {
        MedicalResourceValidation.validateForAssignAccount(row);
        SysUser user = accountAssigner.createAccount(row, operator);
        row.setAccountAssigned(MedicalResourceRules.ACCOUNT_ASSIGNED_YES);
        row.setSysUserId(user.getUserId());
        row.setAssignedLoginName(user.getUserName());
        row.setUpdateBy(operator);
        return bizMedicalResourceMapper.updateBizMedicalResource(row);
    }

    private BizMedicalResource requireRow(Long id)
    {
        BizMedicalResource row = bizMedicalResourceMapper.selectBizMedicalResourceById(id);
        if (row == null)
        {
            throw new ServiceException("记录不存在或已删除");
        }
        return row;
    }

    /**
     * 乡镇/村登录人：兽医人员库仅看本镇数据（未填所属乡镇的兽医记录对乡镇账号不可见）。
     * 非兽医类型不按乡镇收窄。
     */
    private void applyTownDataScope(BizMedicalResource query)
    {
        if (query == null)
        {
            return;
        }
        query.getParams().put("dataScope", "");
        Long scopedTownId = townDeptSupport.resolveScopedTownDeptId();
        if (scopedTownId == null)
        {
            return;
        }
        String type = query.getResourceType();
        if (StringUtils.isNotEmpty(type) && !MedicalResourceRules.RESOURCE_TYPE_VET.equals(type))
        {
            return;
        }
        if (MedicalResourceRules.RESOURCE_TYPE_VET.equals(type))
        {
            query.getParams().put("dataScope", " AND town_dept_id = " + scopedTownId + " ");
            return;
        }
        // 未传类型时：兽医按镇过滤，其余类型仍可见
        query.getParams().put("dataScope",
                " AND (resource_type <> '" + MedicalResourceRules.RESOURCE_TYPE_VET
                        + "' OR town_dept_id = " + scopedTownId + ") ");
    }

    /**
     * 兽医人员可选所属乡镇：校验部门并冗余乡镇名；非兽医清空。
     */
    private void applyTownDept(BizMedicalResource row)
    {
        if (row == null)
        {
            return;
        }
        if (!MedicalResourceRules.RESOURCE_TYPE_VET.equals(row.getResourceType()))
        {
            row.setTownDeptId(null);
            row.setTownName(null);
            return;
        }
        if (row.getTownDeptId() == null)
        {
            row.setTownName(null);
            return;
        }
        SysDept town = townDeptSupport.requireReportTown(row.getTownDeptId());
        if (town == null)
        {
            throw new ServiceException("所属乡镇无效");
        }
        row.setTownName(town.getDeptName());
    }

    @Override
    public List<Map<String, Object>> listTownOptions()
    {
        return townDeptSupport.toOptionList();
    }

    private static List<Long> cleanIds(Long[] ids)
    {
        List<Long> cleaned = new ArrayList<>();
        if (ids == null)
        {
            return cleaned;
        }
        for (Long id : ids)
        {
            if (id != null)
            {
                cleaned.add(id);
            }
        }
        return cleaned;
    }

    private void enrichRow(BizMedicalResource row)
    {
        if (row != null)
        {
            MedicalResourceMultiValueCodec.decodeLists(row);
            MedicalResourcePublishStatusResolver.applyDisplayStatus(row);
        }
    }

    private void deleteDiskFile(String resourcePath)
    {
        if (StringUtils.isEmpty(resourcePath) || !resourcePath.startsWith(Constants.RESOURCE_PREFIX))
        {
            return;
        }
        String relative = FileUtils.stripPrefix(resourcePath);
        while (relative.startsWith("/") || relative.startsWith("\\"))
        {
            relative = relative.substring(1);
        }
        String full = RuoYiConfig.getProfile() + File.separator + relative.replace("/", File.separator);
        FileUtils.deleteFile(full);
    }
}
