package com.bcxin.tenant.open.domains.services.impls;

import com.bcxin.tenant.open.document.domains.documents.RdCompanyDocument;
import com.bcxin.tenant.open.document.domains.documents.RdEmployeeDocument;
import com.bcxin.tenant.open.document.domains.documents.RdExamSiteRoomDocument;
import com.bcxin.tenant.open.document.domains.documents.RdSecurityStationDocument;
import com.bcxin.tenant.open.document.domains.repositories.RdCompanyDocumentRepository;
import com.bcxin.tenant.open.document.domains.repositories.RdEmployeeDocumentRepository;
import com.bcxin.tenant.open.document.domains.repositories.RdExamSiteRoomDocumentRepository;
import com.bcxin.tenant.open.document.domains.repositories.RdSecurityStationDocumentRepository;
import com.bcxin.tenant.open.domains.entities.TenantContentFollowEntity;
import com.bcxin.tenant.open.domains.repositories.TenantContentFollowRepository;
import com.bcxin.tenant.open.domains.services.TenantContentFollowService;
import com.bcxin.tenant.open.domains.services.commands.CreateTenantContentFollowCommand;
import com.bcxin.tenant.open.domains.services.commands.CancelTenantContentFollowCommand;
import com.bcxin.tenant.open.infrastructure.redis.components.DistributedLockProvider;
import com.bcxin.tenant.open.infrastructures.UnitWork;
import com.bcxin.tenant.open.infrastructures.components.IdWorker;
import com.bcxin.tenant.open.infrastructures.enums.ContentFollowType;
import com.bcxin.tenant.open.infrastructures.exceptions.BadTenantException;
import com.bcxin.tenant.open.infrastructures.exceptions.NoFoundTenantException;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class TenantContentFollowServiceImpl implements TenantContentFollowService {
    private final TenantContentFollowRepository tenantContentFollowRepository;
    private final RdEmployeeDocumentRepository employeeDocumentRepository;
    private final RdSecurityStationDocumentRepository securityStationDocumentRepository;

    private final RdExamSiteRoomDocumentRepository rdExamSiteRoomDocumentRepository;
    private final DistributedLockProvider distributedLockProvider;

    private final RdCompanyDocumentRepository companyDocumentRepository;
    private final UnitWork unitWork;
    private final IdWorker idWorker;

    public TenantContentFollowServiceImpl(TenantContentFollowRepository tenantContentFollowRepository,
                                          RdEmployeeDocumentRepository employeeDocumentRepository,
                                          RdSecurityStationDocumentRepository securityStationDocumentRepository,
                                          RdCompanyDocumentRepository companyDocumentRepository,
                                          RdExamSiteRoomDocumentRepository rdExamSiteRoomDocumentRepository, DistributedLockProvider distributedLockProvider,
                                          UnitWork unitWork, IdWorker idWorker) {
        this.tenantContentFollowRepository = tenantContentFollowRepository;
        this.employeeDocumentRepository = employeeDocumentRepository;
        this.securityStationDocumentRepository = securityStationDocumentRepository;
        this.rdExamSiteRoomDocumentRepository = rdExamSiteRoomDocumentRepository;
        this.companyDocumentRepository = companyDocumentRepository;
        this.distributedLockProvider = distributedLockProvider;
        this.unitWork = unitWork;
        this.idWorker = idWorker;
    }

    @Override
    public void dispatch(CreateTenantContentFollowCommand command) {
        command.validate();

        String distributedLockKey = getDistributedLockKey(command.getContentFollowId(), command.getFollowType());
        switch (command.getFollowType()) {
            case Employee -> {
                Optional<RdEmployeeDocument> employeeDocumentOptional
                        = this.employeeDocumentRepository.findById(command.getContentFollowId());
                if (!employeeDocumentOptional.isPresent()) {
                    throw new NoFoundTenantException("找不到指定的驻勤点");
                }
                RdEmployeeDocument document = employeeDocumentOptional.get();

                if (document.addFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.employeeDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
            case Station -> {
                Optional<RdSecurityStationDocument> securityStationDocumentOptional =
                        this.securityStationDocumentRepository.findById(command.getContentFollowId());

                if (!securityStationDocumentOptional.isPresent()) {
                    throw new NoFoundTenantException("找不到指定的驻勤点");
                }

                RdSecurityStationDocument document = securityStationDocumentOptional.get();

                if (document.addFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.securityStationDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
            case Company -> {
                Optional<RdCompanyDocument> companyDocumentOptional
                        = this.companyDocumentRepository.findById(command.getContentFollowId());
                if (!companyDocumentOptional.isPresent()) {
                    throw new NoFoundTenantException("找不到指定的单位");
                }
                RdCompanyDocument document = companyDocumentOptional.get();

                if (document.addFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.companyDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
            case ExamSiteRoom -> {
                RdExamSiteRoomDocument document
                        = this.rdExamSiteRoomDocumentRepository.findById(command.getContentFollowId())
                        .orElse(null);
                if (document == null) {
                    throw new NoFoundTenantException("找不到该考场信息");
                }

                if (document.addFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.rdExamSiteRoomDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            TenantContentFollowEntity contentFollow
                    = TenantContentFollowEntity.create(
                    idWorker.getNextId(),
                    command.getFollowType(),
                    command.getContentFollowId(),
                    command.getDeviceNo(),
                    command.getFollowUserId()
            );
            this.tenantContentFollowRepository.upset(contentFollow);

            this.unitWork.commit(tranId);
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);

            throw new BadTenantException("无法提交关注信息数据", ex);
        }
    }

    @Override
    public void dispatch(CancelTenantContentFollowCommand command) {
        command.validate();
        String distributedLockKey = getDistributedLockKey(command.getContentFollowId(), command.getFollowType());
        switch (command.getFollowType()) {
            case Employee -> {
                Optional<RdEmployeeDocument> employeeDocumentOptional
                        = this.employeeDocumentRepository.findById(command.getContentFollowId());
                if (!employeeDocumentOptional.isPresent()) {
                    throw new NoFoundTenantException("找不到指定的职员信息");
                }
                RdEmployeeDocument document = employeeDocumentOptional.get();
                if (document.removeFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.employeeDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
            case Station -> {
                Optional<RdSecurityStationDocument> securityStationDocumentOptional =
                        this.securityStationDocumentRepository.findById(command.getContentFollowId());

                if (!securityStationDocumentOptional.isPresent()) {
                    throw new NoFoundTenantException("找不到指定的驻勤点");
                }

                RdSecurityStationDocument document = securityStationDocumentOptional.get();
                if (document.removeFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.securityStationDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
            case Company -> {
                Optional<RdCompanyDocument> companyDocumentOptional
                        = this.companyDocumentRepository.findById(command.getContentFollowId());
                if (!companyDocumentOptional.isPresent()) {
                    throw new NoFoundTenantException("找不到指定的单位");
                }
                RdCompanyDocument document = companyDocumentOptional.get();

                if (document.removeFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.companyDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
            
            case ExamSiteRoom -> {
                RdExamSiteRoomDocument document
                        = this.rdExamSiteRoomDocumentRepository.findById(command.getContentFollowId())
                        .orElse(null);
                if (document == null) {
                    throw new NoFoundTenantException("找不到该考场信息");
                }

                if (document.removeFollowedDeviceNo(command.getDeviceNo())) {
                    this.distributedLockProvider.lock(distributedLockKey);
                    try {
                        this.rdExamSiteRoomDocumentRepository.save(document);
                    } finally {
                        this.distributedLockProvider.release(distributedLockKey);
                    }
                }
            }
        }

        TenantContentFollowEntity contentFollow =
                this.tenantContentFollowRepository.findByUniqueValue(
                        command.getFollowType(),
                        command.getContentFollowId(),
                        command.getDeviceNo()
                );

        if (contentFollow == null) {
            throw new NoFoundTenantException(String.format("无法找到关注(%s)的内容", command.getContentFollowId()));
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            contentFollow.markUnfollowed(command.getFollowUserId());
            this.tenantContentFollowRepository.upset(contentFollow);

            this.unitWork.commit(tranId);
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);

            throw new BadTenantException("取消关注发生异常", ex);
        }
    }

    private static String getDistributedLockKey(String contentFollowId, ContentFollowType followType) {
        String distributedLockKey = String.format("tfm_%s_%s", contentFollowId, followType);

        return distributedLockKey;
    }
}
