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

import com.bcxin.tenant.open.domains.entities.RollCallPlanEntity;
import com.bcxin.tenant.open.domains.pojo.RollCallPlanTimerPoJo;
import com.bcxin.tenant.open.domains.repositories.RollCallPlanRepository;
import com.bcxin.tenant.open.domains.services.RollCallPlanService;
import com.bcxin.tenant.open.domains.services.commands.CreateRollCallPlanCommand;
import com.bcxin.tenant.open.domains.services.commands.DeleteRollCallPlanCommand;
import com.bcxin.tenant.open.domains.services.commands.UpdateRollCallPlanCommand;
import com.bcxin.tenant.open.infrastructures.TenantContext;
import com.bcxin.tenant.open.infrastructures.TenantEmployeeContext;
import com.bcxin.tenant.open.infrastructures.UnitWork;
import com.bcxin.tenant.open.infrastructures.components.IdWorker;
import com.bcxin.tenant.open.infrastructures.components.JsonProvider;
import com.bcxin.tenant.open.infrastructures.enums.RepetitiveRule;
import com.bcxin.tenant.open.infrastructures.exceptions.NoFoundTenantException;
import com.bcxin.tenant.open.infrastructures.exceptions.UnAuthorizedTenantException;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.HashSet;
import java.util.stream.Collectors;

@Service
public class RollCallPlanServiceImpl implements RollCallPlanService {

    private final UnitWork unitWork;
    private final RollCallPlanRepository rollCallPlanRepository;
    private final JsonProvider jsonProvider;
    private final IdWorker idWorker;

    public RollCallPlanServiceImpl(UnitWork unitWork, RollCallPlanRepository rollCallPlanRepository, JsonProvider jsonProvider, IdWorker idWorker) {
        this.unitWork = unitWork;
        this.rollCallPlanRepository = rollCallPlanRepository;
        this.jsonProvider = jsonProvider;
        this.idWorker = idWorker;
    }

    @Override
    public void dispatch(CreateRollCallPlanCommand command) {
        TenantEmployeeContext.TenantUserModel userModel = TenantContext.getInstance().getUserContext().get();
        if (userModel == null) {
            throw new UnAuthorizedTenantException();
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            long id = this.idWorker.getNextId();

            Collection<RollCallPlanTimerPoJo> timerPoJos = new HashSet<>();
            if (!CollectionUtils.isEmpty(command.getTimers())) {
                timerPoJos = command.getTimers().stream().map(ii -> RollCallPlanTimerPoJo.create(ii)).collect(Collectors.toList());
            }

            String timerJson = this.jsonProvider.getJson(timerPoJos);
            String stationTypeJson = this.jsonProvider.getJson(command.getStationTypes());
            RollCallPlanEntity rollCallPlanEntity =
                    RollCallPlanEntity.create(
                            id,
                            command.getName(),
                            command.getBeginDate(),
                            command.getEndDate(),
                            command.getRepetitiveRules(),
                            command.getSuperviseDepartId(),
                            command.getSuperviseDepartName(),
                            stationTypeJson,
                            timerJson,
                            userModel.getOrganizationId(),
                            userModel.getOrgInstitutional(),
                            userModel.getOrgName(),
                            userModel.getEmployeeId(),
                            userModel.getName());

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

            throw ex;
        }
    }

    @Override
    public void dispatch(UpdateRollCallPlanCommand command) {
        TenantEmployeeContext.TenantUserModel userModel = TenantContext.getInstance().getUserContext().get();
        if (userModel == null) {
            throw new UnAuthorizedTenantException();
        }

        RollCallPlanEntity rollCallPlan =
                this.rollCallPlanRepository.getById(command.getId());
        if (rollCallPlan == null) {
            throw new NoFoundTenantException();
        }

        if(rollCallPlan.isDeleted()) {
            throw new NoFoundTenantException();
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            String stationTypeJson = this.jsonProvider.getJson(command.getStationTypes());

            Collection<RollCallPlanTimerPoJo> timerPoJos = new HashSet<>();
            if(!CollectionUtils.isEmpty(command.getTimers())) {
                timerPoJos = command.getTimers().stream().map(ii -> RollCallPlanTimerPoJo.create(ii)).collect(Collectors.toList());
            }

            String timerJson = this.jsonProvider.getJson(timerPoJos);
            rollCallPlan.change(
                    command.getName(),
                    command.getBeginDate(),
                    command.getEndDate(),
                    command.getRepetitiveRules(),
                    command.getSuperviseDepartId(),
                    command.getSuperviseDepartName(),
                    stationTypeJson,
                    timerJson,
                    userModel.getEmployeeId(),
                    userModel.getName()
            );
            this.rollCallPlanRepository.update(rollCallPlan);
            this.unitWork.commit(tranId);
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);

            throw ex;
        }
    }

    @Override
    public void dispatch(DeleteRollCallPlanCommand command) {
        RollCallPlanEntity rollCallPlan =
                this.rollCallPlanRepository.getById(command.getId());
        if (rollCallPlan == null) {
            throw new NoFoundTenantException();
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            rollCallPlan.markDeleted();
            this.rollCallPlanRepository.update(rollCallPlan);
            this.unitWork.commit(tranId);
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);

            throw ex;
        }
    }
}
