package com.haha.service.impl;

import cn.dev33.satoken.stp.StpUtil;
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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haha.common.enums.*;
import com.haha.common.exception.BusinessException;
import com.haha.entity.Shop;
import com.haha.entity.ShopArchive;
import com.haha.entity.ShopContractAttachment;
import com.haha.entity.ShopFixedAsset;
import com.haha.entity.dto.*;
import com.haha.mapper.ShopArchiveMapper;
import com.haha.mapper.ShopContractAttachmentMapper;
import com.haha.mapper.ShopFixedAssetMapper;
import com.haha.mapper.ShopMapper;
import com.haha.service.ShopArchiveService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 门店档案服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ShopArchiveServiceImpl extends ServiceImpl<ShopArchiveMapper, ShopArchive> implements ShopArchiveService {

    private final ShopArchiveMapper shopArchiveMapper;
    private final ShopFixedAssetMapper shopFixedAssetMapper;
    private final ShopContractAttachmentMapper shopContractAttachmentMapper;
    private final ShopMapper shopMapper;

    @Override
    public IPage<ShopArchive> getPage(ShopArchiveQueryDTO queryDTO) {
        queryDTO.validate();
        Page<ShopArchive> page = new Page<>(queryDTO.getPage(), queryDTO.getPageSize());

        LambdaQueryWrapper<ShopArchive> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.hasText(queryDTO.getShopName()), ShopArchive::getShopName, queryDTO.getShopName());
        wrapper.eq(StringUtils.hasText(queryDTO.getContractNo()), ShopArchive::getContractNo, queryDTO.getContractNo());
        wrapper.eq(StringUtils.hasText(queryDTO.getContractStatus()), ShopArchive::getContractStatus, queryDTO.getContractStatus());
        wrapper.eq(StringUtils.hasText(queryDTO.getBusinessStatus()), ShopArchive::getBusinessStatus, queryDTO.getBusinessStatus());
        wrapper.eq(StringUtils.hasText(queryDTO.getBillingMode()), ShopArchive::getBillingMode, queryDTO.getBillingMode());
        wrapper.eq(StringUtils.hasText(queryDTO.getVenueType()), ShopArchive::getVenueType, queryDTO.getVenueType());
        wrapper.eq(queryDTO.getShopId() != null, ShopArchive::getShopId, queryDTO.getShopId());
        wrapper.orderByDesc(ShopArchive::getCreateTime);

        IPage<ShopArchive> result = this.page(page, wrapper);

        // 填充标签
        for (ShopArchive archive : result.getRecords()) {
            fillLabels(archive);
        }

        return result;
    }

    @Override
    public ShopArchive getDetailById(Long id) {
        ShopArchive archive = this.getById(id);
        if (archive == null) {
            throw new BusinessException(404, "门店档案不存在");
        }

        // 查询固定资产
        List<ShopFixedAsset> fixedAssets = shopFixedAssetMapper.selectList(
                new LambdaQueryWrapper<ShopFixedAsset>()
                        .eq(ShopFixedAsset::getShopArchiveId, id)
                        .orderByAsc(ShopFixedAsset::getCreateTime)
        );
        archive.setFixedAssets(fixedAssets);

        // 查询附件
        List<ShopContractAttachment> attachments = shopContractAttachmentMapper.selectList(
                new LambdaQueryWrapper<ShopContractAttachment>()
                        .eq(ShopContractAttachment::getShopArchiveId, id)
                        .orderByAsc(ShopContractAttachment::getSortOrder)
        );
        archive.setAttachments(attachments);

        // 填充标签
        fillLabels(archive);

        // 填充资产标签
        if (fixedAssets != null) {
            for (ShopFixedAsset asset : fixedAssets) {
                asset.setAssetTypeLabel(AssetType.getDescription(asset.getAssetType()));
                asset.setAssetStatusLabel(AssetStatus.getDescription(asset.getAssetStatus()));
            }
        }

        // 填充附件标签
        if (attachments != null) {
            for (ShopContractAttachment attachment : attachments) {
                attachment.setAttachmentTypeLabel(AttachmentType.getDescription(attachment.getAttachmentType()));
            }
        }

        return archive;
    }

    @Override
    public ShopArchive getByShopId(Long shopId) {
        ShopArchive archive = shopArchiveMapper.selectByShopId(shopId);
        if (archive != null) {
            // 查询固定资产
            List<ShopFixedAsset> fixedAssets = shopFixedAssetMapper.selectList(
                    new LambdaQueryWrapper<ShopFixedAsset>()
                            .eq(ShopFixedAsset::getShopArchiveId, archive.getId())
                            .orderByAsc(ShopFixedAsset::getCreateTime)
            );
            archive.setFixedAssets(fixedAssets);

            // 查询附件
            List<ShopContractAttachment> attachments = shopContractAttachmentMapper.selectList(
                    new LambdaQueryWrapper<ShopContractAttachment>()
                            .eq(ShopContractAttachment::getShopArchiveId, archive.getId())
                            .orderByAsc(ShopContractAttachment::getSortOrder)
            );
            archive.setAttachments(attachments);

            fillLabels(archive);
        }
        return archive;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ShopArchive create(ShopArchiveCreateDTO dto) {
        // 检查门店是否存在
        Shop shop = shopMapper.selectById(dto.getShopId());
        if (shop == null) {
            throw new BusinessException(404, "门店不存在");
        }

        // 检查档案是否已存在
        ShopArchive existArchive = this.lambdaQuery()
                .eq(ShopArchive::getShopId, dto.getShopId())
                .one();
        if (existArchive != null) {
            throw new BusinessException(400, "该门店已存在档案");
        }

        // 构建档案实体
        ShopArchive archive = new ShopArchive();
        archive.setShopId(dto.getShopId());
        archive.setShopName(dto.getShopName());
        archive.setShopAddress(dto.getShopAddress());
        archive.setVenueType(dto.getVenueType());
        archive.setCooperationMode(dto.getCooperationMode());
        archive.setPartyBSalesman(dto.getPartyBSalesman());
        archive.setPartyBSalesmanPhone(dto.getPartyBSalesmanPhone());
        archive.setPartyACompany(dto.getPartyACompany());
        archive.setPartyAManager(dto.getPartyAManager());
        archive.setPartyAManagerPhone(dto.getPartyAManagerPhone());
        archive.setBusinessStatus(dto.getBusinessStatus());
        archive.setRemark(dto.getRemark());
        archive.setContractNo(dto.getContractNo());
        archive.setContractSignDate(dto.getContractSignDate());
        archive.setContractStartDate(dto.getContractStartDate());
        archive.setContractEndDate(dto.getContractEndDate());
        archive.setContractStatus(dto.getContractStatus());
        archive.setRentStartDate(dto.getRentStartDate());
        archive.setMonthlyRent(dto.getMonthlyRent());
        archive.setRentFreeDays(dto.getRentFreeDays());
        archive.setRentPayDeadlineDay(dto.getRentPayDeadlineDay());
        archive.setDepositAmount(dto.getDepositAmount());
        archive.setDepositStatus(dto.getDepositStatus());
        archive.setDepositPaidAmount(dto.getDepositPaidAmount());
        archive.setDepositRefundCondition(dto.getDepositRefundCondition());
        archive.setBillingMode(dto.getBillingMode());
        archive.setElectricityPrice(dto.getElectricityPrice());
        archive.setElectricityShareRule(dto.getElectricityShareRule());
        archive.setCommissionBaseType(dto.getCommissionBaseType());
        archive.setCommissionBase(dto.getCommissionBase());
        archive.setCommissionRate(dto.getCommissionRate());
        archive.setCommissionMinGuarantee(dto.getCommissionMinGuarantee());
        archive.setCommissionPeriodMonths(dto.getCommissionPeriodMonths());
        archive.setPaymentCycle(dto.getPaymentCycle());
        archive.setTerminationDate(dto.getTerminationDate());
        archive.setTerminationRemark(dto.getTerminationRemark());
        archive.setPaymentMethod(dto.getPaymentMethod());
        archive.setPayeeAccountName(dto.getPayeeAccountName());
        archive.setPayeeBankName(dto.getPayeeBankName());
        archive.setPayeeBankAccount(dto.getPayeeBankAccount());
        archive.setTaxpayerId(dto.getTaxpayerId());
        archive.setInvoiceType(dto.getInvoiceType());
        archive.setInvoiceTaxRate(dto.getInvoiceTaxRate());

        // 设置门店编码
        archive.setShopCode(shop.getShopCode());

        // 设置创建人信息
        Long currentUserId = StpUtil.getLoginIdAsLong();
        archive.setCreatorId(currentUserId);
        archive.setCreatorName(StpUtil.getLoginIdAsString());
        archive.setCreateTime(LocalDateTime.now());
        archive.setUpdateTime(LocalDateTime.now());

        // 保存档案
        this.save(archive);

        // 保存固定资产
        saveFixedAssets(archive.getId(), archive.getShopId(), dto.getFixedAssets(), currentUserId);

        // 保存附件
        saveAttachments(archive.getId(), archive.getShopId(), dto.getAttachments(), currentUserId);

        log.info("创建门店档案成功: id={}, shopId={}, shopName={}", archive.getId(), archive.getShopId(), archive.getShopName());

        return this.getDetailById(archive.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ShopArchive update(ShopArchiveUpdateDTO dto) {
        // 检查档案是否存在
        ShopArchive archive = this.getById(dto.getId());
        if (archive == null) {
            throw new BusinessException(404, "门店档案不存在");
        }

        // 检查门店是否存在
        Shop shop = shopMapper.selectById(dto.getShopId());
        if (shop == null) {
            throw new BusinessException(404, "门店不存在");
        }

        // 更新档案字段
        archive.setShopId(dto.getShopId());
        archive.setShopName(dto.getShopName());
        archive.setShopAddress(dto.getShopAddress());
        archive.setVenueType(dto.getVenueType());
        archive.setCooperationMode(dto.getCooperationMode());
        archive.setPartyBSalesman(dto.getPartyBSalesman());
        archive.setPartyBSalesmanPhone(dto.getPartyBSalesmanPhone());
        archive.setPartyACompany(dto.getPartyACompany());
        archive.setPartyAManager(dto.getPartyAManager());
        archive.setPartyAManagerPhone(dto.getPartyAManagerPhone());
        archive.setBusinessStatus(dto.getBusinessStatus());
        archive.setRemark(dto.getRemark());
        archive.setContractNo(dto.getContractNo());
        archive.setContractSignDate(dto.getContractSignDate());
        archive.setContractStartDate(dto.getContractStartDate());
        archive.setContractEndDate(dto.getContractEndDate());
        archive.setContractStatus(dto.getContractStatus());
        archive.setRentStartDate(dto.getRentStartDate());
        archive.setMonthlyRent(dto.getMonthlyRent());
        archive.setRentFreeDays(dto.getRentFreeDays());
        archive.setRentPayDeadlineDay(dto.getRentPayDeadlineDay());
        archive.setDepositAmount(dto.getDepositAmount());
        archive.setDepositStatus(dto.getDepositStatus());
        archive.setDepositPaidAmount(dto.getDepositPaidAmount());
        archive.setDepositRefundCondition(dto.getDepositRefundCondition());
        archive.setBillingMode(dto.getBillingMode());
        archive.setElectricityPrice(dto.getElectricityPrice());
        archive.setElectricityShareRule(dto.getElectricityShareRule());
        archive.setCommissionBaseType(dto.getCommissionBaseType());
        archive.setCommissionBase(dto.getCommissionBase());
        archive.setCommissionRate(dto.getCommissionRate());
        archive.setCommissionMinGuarantee(dto.getCommissionMinGuarantee());
        archive.setCommissionPeriodMonths(dto.getCommissionPeriodMonths());
        archive.setPaymentCycle(dto.getPaymentCycle());
        archive.setTerminationDate(dto.getTerminationDate());
        archive.setTerminationRemark(dto.getTerminationRemark());
        archive.setPaymentMethod(dto.getPaymentMethod());
        archive.setPayeeAccountName(dto.getPayeeAccountName());
        archive.setPayeeBankName(dto.getPayeeBankName());
        archive.setPayeeBankAccount(dto.getPayeeBankAccount());
        archive.setTaxpayerId(dto.getTaxpayerId());
        archive.setInvoiceType(dto.getInvoiceType());
        archive.setInvoiceTaxRate(dto.getInvoiceTaxRate());

        // 更新门店编码
        archive.setShopCode(shop.getShopCode());

        // 设置编辑人信息
        Long currentUserId = StpUtil.getLoginIdAsLong();
        archive.setEditorId(currentUserId);
        archive.setEditorName(StpUtil.getLoginIdAsString());
        archive.setUpdateTime(LocalDateTime.now());

        // 更新档案
        this.updateById(archive);

        // 更新固定资产：先删除再新增
        shopFixedAssetMapper.delete(
                new LambdaQueryWrapper<ShopFixedAsset>()
                        .eq(ShopFixedAsset::getShopArchiveId, archive.getId())
        );
        saveFixedAssets(archive.getId(), archive.getShopId(), dto.getFixedAssets(), currentUserId);

        // 更新附件：对比处理
        updateAttachments(archive.getId(), archive.getShopId(), dto.getAttachments(), currentUserId);

        log.info("更新门店档案成功: id={}, shopId={}, shopName={}", archive.getId(), archive.getShopId(), archive.getShopName());

        return this.getDetailById(archive.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        ShopArchive archive = this.getById(id);
        if (archive == null) {
            throw new BusinessException(404, "门店档案不存在");
        }

        // 删除固定资产
        shopFixedAssetMapper.delete(
                new LambdaQueryWrapper<ShopFixedAsset>()
                        .eq(ShopFixedAsset::getShopArchiveId, id)
        );

        // 删除附件
        shopContractAttachmentMapper.delete(
                new LambdaQueryWrapper<ShopContractAttachment>()
                        .eq(ShopContractAttachment::getShopArchiveId, id)
        );

        // 删除档案
        this.removeById(id);

        log.info("删除门店档案成功: id={}, shopId={}", id, archive.getShopId());
    }

    @Override
    public List<Map<String, String>> getEnumOptions(String enumType) {
        List<Map<String, String>> options = new ArrayList<>();

        switch (enumType) {
            case "venueType":
                for (VenueType e : VenueType.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "businessStatus":
                for (BusinessStatus e : BusinessStatus.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "contractStatus":
                for (ContractStatus e : ContractStatus.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "depositStatus":
                for (DepositStatus e : DepositStatus.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "billingMode":
                for (BillingMode e : BillingMode.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "paymentCycle":
                for (PaymentCycle e : PaymentCycle.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "assetType":
                for (AssetType e : AssetType.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "assetStatus":
                for (AssetStatus e : AssetStatus.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "attachmentType":
                for (AttachmentType e : AttachmentType.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "paymentMethod":
                for (PaymentMethod e : PaymentMethod.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "invoiceType":
                for (InvoiceType e : InvoiceType.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            case "commissionBaseType":
                for (CommissionBaseType e : CommissionBaseType.values()) {
                    options.add(createOption(e.getCode(), e.getDescription()));
                }
                break;
            default:
                throw new BusinessException(400, "不支持的枚举类型: " + enumType);
        }

        return options;
    }

    /**
     * 填充档案标签
     */
    private void fillLabels(ShopArchive archive) {
        if (archive.getContractStatus() != null) {
            archive.setContractStatusLabel(ContractStatus.getDescription(archive.getContractStatus()));
        }
        if (archive.getBusinessStatus() != null) {
            archive.setBusinessStatusLabel(BusinessStatus.getDescription(archive.getBusinessStatus()));
        }
    }

    /**
     * 保存固定资产
     */
    private void saveFixedAssets(Long archiveId, Long shopId, List<FixedAssetDTO> dtos, Long currentUserId) {
        if (dtos == null || dtos.isEmpty()) {
            return;
        }

        for (FixedAssetDTO dto : dtos) {
            ShopFixedAsset asset = new ShopFixedAsset();
            asset.setShopArchiveId(archiveId);
            asset.setShopId(shopId);
            asset.setAssetType(dto.getAssetType());
            asset.setAssetName(dto.getAssetName());
            asset.setAssetCode(dto.getAssetCode());
            asset.setAssetStatus(dto.getAssetStatus());
            asset.setRemark(dto.getRemark());
            asset.setCreatorId(currentUserId);
            asset.setCreatorName(StpUtil.getLoginIdAsString());
            asset.setCreateTime(LocalDateTime.now());
            asset.setUpdateTime(LocalDateTime.now());
            shopFixedAssetMapper.insert(asset);
        }
    }

    /**
     * 保存附件
     */
    private void saveAttachments(Long archiveId, Long shopId, List<AttachmentDTO> dtos, Long currentUserId) {
        if (dtos == null || dtos.isEmpty()) {
            return;
        }

        for (AttachmentDTO dto : dtos) {
            ShopContractAttachment attachment = new ShopContractAttachment();
            attachment.setShopArchiveId(archiveId);
            attachment.setShopId(shopId);
            attachment.setAttachmentType(dto.getAttachmentType());
            attachment.setFileName(dto.getFileName());
            attachment.setFileUrl(dto.getFileUrl());
            attachment.setFileSize(dto.getFileSize());
            attachment.setFileExtension(dto.getFileExtension());
            attachment.setSortOrder(dto.getSortOrder());
            attachment.setCreatorId(currentUserId);
            attachment.setCreatorName(StpUtil.getLoginIdAsString());
            attachment.setCreateTime(LocalDateTime.now());
            shopContractAttachmentMapper.insert(attachment);
        }
    }

    /**
     * 更新附件（对比处理）
     */
    private void updateAttachments(Long archiveId, Long shopId, List<AttachmentDTO> dtos, Long currentUserId) {
        // 查询现有附件
        List<ShopContractAttachment> existingAttachments = shopContractAttachmentMapper.selectList(
                new LambdaQueryWrapper<ShopContractAttachment>()
                        .eq(ShopContractAttachment::getShopArchiveId, archiveId)
        );

        Set<Long> existingIds = existingAttachments.stream()
                .map(ShopContractAttachment::getId)
                .collect(Collectors.toSet());

        Set<Long> dtoIds = dtos != null ? dtos.stream()
                .map(AttachmentDTO::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet()) : Collections.emptySet();

        // 删除不在DTO中的附件
        for (Long existingId : existingIds) {
            if (!dtoIds.contains(existingId)) {
                shopContractAttachmentMapper.deleteById(existingId);
            }
        }

        // 新增或更新附件
        if (dtos != null) {
            for (AttachmentDTO dto : dtos) {
                if (dto.getId() == null) {
                    // 新增
                    ShopContractAttachment attachment = new ShopContractAttachment();
                    attachment.setShopArchiveId(archiveId);
                    attachment.setShopId(shopId);
                    attachment.setAttachmentType(dto.getAttachmentType());
                    attachment.setFileName(dto.getFileName());
                    attachment.setFileUrl(dto.getFileUrl());
                    attachment.setFileSize(dto.getFileSize());
                    attachment.setFileExtension(dto.getFileExtension());
                    attachment.setSortOrder(dto.getSortOrder());
                    attachment.setCreatorId(currentUserId);
                    attachment.setCreatorName(StpUtil.getLoginIdAsString());
                    attachment.setCreateTime(LocalDateTime.now());
                    shopContractAttachmentMapper.insert(attachment);
                } else if (existingIds.contains(dto.getId())) {
                    // 更新
                    ShopContractAttachment attachment = shopContractAttachmentMapper.selectById(dto.getId());
                    if (attachment != null) {
                        attachment.setAttachmentType(dto.getAttachmentType());
                        attachment.setFileName(dto.getFileName());
                        attachment.setFileUrl(dto.getFileUrl());
                        attachment.setFileSize(dto.getFileSize());
                        attachment.setFileExtension(dto.getFileExtension());
                        attachment.setSortOrder(dto.getSortOrder());
                        shopContractAttachmentMapper.updateById(attachment);
                    }
                }
            }
        }
    }

    /**
     * 创建选项Map
     */
    private Map<String, String> createOption(String code, String description) {
        Map<String, String> option = new HashMap<>();
        option.put("code", code);
        option.put("description", description);
        return option;
    }
}
