package xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.notification;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xyz.zhangyi.ddd.core.stereotype.DomainService;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.south.port.repository.MailTemplateRepository;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.ticket.Nominator;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.ticket.Nominee;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.ticket.Ticket;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.training.Training;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.training.TrainingException;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.south.port.repository.TrainingRepository;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.validate.ValidDate;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.validate.ValidDateException;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.south.port.repository.ValidDateRepository;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.domain.validate.ValidDateType;
import xyz.zhangyi.ddd.eas.valueadded.trainingcontext.south.port.client.NotificationClient;

import java.util.Optional;

@Service
@DomainService
public class NotificationService {
    @Autowired
    private MailTemplateRepository templateRepository;
    @Autowired
    private NotificationClient notificationClient;
    @Autowired
    private TrainingRepository trainingRepository;
    @Autowired
    private ValidDateRepository validDateRepository;

    public void notifyNominee(Ticket ticket, Nominator nominator, Nominee nominee) {
        MailTemplate mailTemplate = retrieveMailTemplate();
        Training training = retrieveTraining(ticket);
        ValidDate validDate = retrieveValidDate(ticket);

        VariableContext variableContext = buildVariableContext(ticket, nominator, nominee, training, validDate);
        Notification notification = mailTemplate.compose(variableContext);

        notificationClient.send(notification);
    }

    private MailTemplate retrieveMailTemplate() {
        Optional<MailTemplate> optionalMailTemplate = templateRepository.templateOf(TemplateType.Nomination);
        String mailTemplateNotFoundMessage = String.format("mail template by %s was not found.", TemplateType.Nomination);
        return optionalMailTemplate.orElseThrow(() -> new MailTemplateException(mailTemplateNotFoundMessage));
    }

    private Training retrieveTraining(Ticket ticket) {
        Optional<Training> optionalTraining = trainingRepository.trainingOf(ticket.trainingId());
        String trainingNotFoundMessage = String.format("training by id {%s} was not found.", ticket.trainingId());
        return optionalTraining.orElseThrow(() -> new TrainingException(trainingNotFoundMessage));
    }

    private ValidDate retrieveValidDate(Ticket ticket) {
        Optional<ValidDate> optionalValidDate = validDateRepository.validDateOf(ticket.trainingId(), ValidDateType.PODeadline);
        String validDateNotFoundMessage = String.format("valid date by training id {%s} was not found.", ticket.trainingId());
        return optionalValidDate.orElseThrow(() -> new ValidDateException(validDateNotFoundMessage));
    }

    private VariableContext buildVariableContext(Ticket ticket, Nominator nominator, Nominee nominee, Training training, ValidDate validDate) {
        VariableContext variableContext = new VariableContext();
        variableContext.put("ticket", ticket);
        variableContext.put("training", training);
        variableContext.put("valid_date", validDate);
        variableContext.put("nominator", nominator);
        variableContext.put("nominee", nominee);
        return variableContext;
    }

    public void setMailTemplateRepository(MailTemplateRepository templateRepository) {
        this.templateRepository = templateRepository;
    }

    public void setNotificationClient(NotificationClient notificationClient) {
        this.notificationClient = notificationClient;
    }

    public void setTrainingRepository(TrainingRepository trainingRepository) {
        this.trainingRepository = trainingRepository;
    }

    public void setValidDateRepository(ValidDateRepository validDateRepository) {
        this.validDateRepository = validDateRepository;
    }
}