package ru.ulstu.grant.service;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.deadline.service.DeadlineService;
import ru.ulstu.file.service.FileService;
import ru.ulstu.grant.model.Grant;
import ru.ulstu.grant.model.GrantDto;
import ru.ulstu.grant.repository.GrantRepository;
import ru.ulstu.project.model.Project;
import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.project.service.ProjectService;

import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert;
import static ru.ulstu.grant.model.Grant.GrantStatus.APPLICATION;

@Service
public class GrantService {
    private final static int MAX_DISPLAY_SIZE = 40;

    private final GrantRepository grantRepository;
    private final ProjectService projectService;
    private final DeadlineService deadlineService;
    private final FileService fileService;

    public GrantService(GrantRepository grantRepository,
                        FileService fileService,
                        DeadlineService deadlineService,
                        ProjectService projectService) {
        this.grantRepository = grantRepository;
        this.projectService = projectService;
        this.fileService = fileService;
        this.deadlineService = deadlineService;
    }

    public List<Grant> findAll() {
        return grantRepository.findAll();
    }

    public List<GrantDto> findAllDto() {
        List<GrantDto> grants = convert(findAll(), GrantDto::new);
        grants.forEach(grantDto -> grantDto.setTitle(StringUtils.abbreviate(grantDto.getTitle(), MAX_DISPLAY_SIZE)));
        return grants;
    }

    public GrantDto findOneDto(Integer id) {
        return new GrantDto(grantRepository.findOne(id));
    }

    @Transactional
    public Integer create(GrantDto grantDto) throws IOException {
        Grant newGrant = copyFromDto(new Grant(), grantDto);
        newGrant = grantRepository.save(newGrant);
        return newGrant.getId();
    }

    private Grant copyFromDto(Grant grant, GrantDto grantDto) throws IOException {
        grant.setComment(grantDto.getComment());
        grant.setStatus(grantDto.getStatus() == null ? APPLICATION : grantDto.getStatus());
        grant.setTitle(grantDto.getTitle());
        if (grantDto.getProject() != null && grantDto.getProject().getId() != null) {
            grant.setProject(projectService.findById(grantDto.getProject().getId()));
        }
        grant.setDeadlines(deadlineService.saveOrCreate(grantDto.getDeadlines()));
        if (grantDto.getApplicationFileName() != null) {
            grant.setApplication(fileService.createFileFromTmp(grantDto.getApplicationFileName()));
        }
        return grant;
    }

    public void createProject(GrantDto grantDto) {
        grantDto.setProject(
                new ProjectDto(projectService.save(new ProjectDto(grantDto.getTitle()))));
    }

    @Transactional
    public Integer update(GrantDto grantDto) throws IOException {
        Grant grant = grantRepository.findOne(grantDto.getId());
        Grant.GrantStatus oldStatus = grant.getStatus();
        if (grantDto.getApplicationFileName() != null && grant.getApplication() != null) {
            fileService.deleteFile(grant.getApplication());
        }
        grantRepository.save(copyFromDto(grant, grantDto));
        return grant.getId();
    }

    @Transactional
    public void delete(Integer grantId) throws IOException {
        Grant grant = grantRepository.findOne(grantId);
        if (grant.getApplication() != null) {
            fileService.deleteFile(grant.getApplication());
        }
        //возможно при удалении гранта будет удаляться и проект, к нему привязанный
        grantRepository.delete(grant);
    }

    public List<Grant.GrantStatus> getGrantStatuses() {
        return Arrays.asList(Grant.GrantStatus.values());
    }

    @Transactional
    public Grant create(String title, Project projectId, Date deadlineDate) {
        Grant grant = new Grant();
        grant.setTitle(title);
        grant.setComment("Комментарий к гранту 1");
        grant.setProject(projectId);
        grant.setStatus(APPLICATION);
        grant.getDeadlines().add(new Deadline(deadlineDate, "первый дедлайн"));
        grant = grantRepository.save(grant);
        return grant;
    }

    public void save(GrantDto grantDto) throws IOException {
        if (isEmpty(grantDto.getId())) {
            create(grantDto);
        } else {
            update(grantDto);
        }
    }
}
