package ru.yandex.direct.web.entity.agencyofflinereport.service;

import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.ParametersAreNonnullByDefault;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import ru.yandex.direct.common.TranslationService;
import ru.yandex.direct.core.ErrorCodes;
import ru.yandex.direct.core.entity.agencyofflinereport.model.AgencyOfflineReport;
import ru.yandex.direct.core.entity.agencyofflinereport.model.AgencyOfflineReportState;
import ru.yandex.direct.core.entity.agencyofflinereport.service.AgencyOfflineReportService;
import ru.yandex.direct.core.entity.user.model.User;
import ru.yandex.direct.core.security.authorization.Permission;
import ru.yandex.direct.core.validation.CommonDefectTranslations;
import ru.yandex.direct.rbac.RbacRepType;
import ru.yandex.direct.rbac.RbacRole;
import ru.yandex.direct.rbac.RbacService;
import ru.yandex.direct.validation.result.Defect;
import ru.yandex.direct.validation.result.ValidationResult;
import ru.yandex.direct.web.core.model.WebErrorResponse;
import ru.yandex.direct.web.core.model.WebResponse;
import ru.yandex.direct.web.core.model.WebSuccessResponse;
import ru.yandex.direct.web.core.security.DirectWebAuthenticationSource;
import ru.yandex.direct.web.entity.agencyofflinereport.model.AgencyOfflineReportWebParams;
import ru.yandex.direct.web.entity.agencyofflinereport.model.EnqueueReportRequestParams;
import ru.yandex.direct.web.entity.agencyofflinereport.model.GetAvailableReportsResponse;
import ru.yandex.direct.web.validation.kernel.ValidationResultConversionService;
import ru.yandex.direct.web.validation.model.ValidationResponse;

import static org.assertj.core.util.Preconditions.checkState;
import static ru.yandex.direct.core.validation.ValidationUtils.hasValidationIssues;
import static ru.yandex.direct.utils.StringUtils.nullIfBlank;

@Service
@ParametersAreNonnullByDefault
public class AgencyOfflineReportWebService {
    private final AgencyOfflineReportWebValidationService validationService;
    private final AgencyOfflineReportService agencyOfflineReportService;
    private final DirectWebAuthenticationSource directWebAuthenticationSource;
    private final RbacService rbacService;
    private final TranslationService translationService;
    private final ValidationResultConversionService validationResultConversionService;

    @Autowired
    public AgencyOfflineReportWebService(
            AgencyOfflineReportWebValidationService validationService,
            AgencyOfflineReportService agencyOfflineReportService,
            DirectWebAuthenticationSource directWebAuthenticationSource,
            RbacService rbacService, TranslationService translationService,
            ValidationResultConversionService validationResultConversionService) {
        this.validationService = validationService;
        this.agencyOfflineReportService = agencyOfflineReportService;
        this.directWebAuthenticationSource = directWebAuthenticationSource;
        this.rbacService = rbacService;
        this.translationService = translationService;
        this.validationResultConversionService = validationResultConversionService;
    }

    public WebResponse enqueueReport(EnqueueReportRequestParams request) {
        if (!hasRights(Permission.WRITE)) {
            return noRightsResponse();

        }

        ValidationResult<EnqueueReportRequestParams, Defect> vr = validationService.validateEnqueueRequest(request);
        if (hasValidationIssues(vr)) {
            return new ValidationResponse(validationResultConversionService.buildWebValidationResult(vr));
        }

        User operator = directWebAuthenticationSource.getAuthentication().getOperator();
        User agency = directWebAuthenticationSource.getAuthentication().getSubjectUser();
        agencyOfflineReportService.enqueueReportRequest(operator, agency, request.getDateFrom(), request.getDateTo());
        return new WebSuccessResponse();
    }

    public WebResponse listReports() {
        if (!hasRights(Permission.READ)) {
            return noRightsResponse();
        }

        User operator = directWebAuthenticationSource.getAuthentication().getOperator();
        User agency = directWebAuthenticationSource.getAuthentication().getSubjectUser();

        List<AgencyOfflineReportWebParams> reportsList = agencyOfflineReportService
                .getAgencyOfflineReports(operator, agency)
                .stream()
                .map(AgencyOfflineReportWebService::convertOfflineReportToWebOfflineReport)
                .collect(Collectors.toList());

        return new GetAvailableReportsResponse().withResult(reportsList);
    }

    public String getReportDownloadUrl(Long reportId) {
        if (!validationService.isValidReportId(reportId)) {
            return null;
        }
        if (!hasRights(Permission.READ)) {
            return null;
        }

        User agency = directWebAuthenticationSource.getAuthentication().getSubjectUser();
        AgencyOfflineReport report = agencyOfflineReportService.getAgencyOfflineReport(agency.getClientId(), reportId);
        if (report == null || report.getReportState() != AgencyOfflineReportState.READY) {
            return null;
        }

        return nullIfBlank(report.getReportUrl());
    }

    boolean hasRights(Permission permission) {
        checkState(permission == Permission.READ || permission == Permission.WRITE,
                "Unsupported permission %s", permission);

        User operator = directWebAuthenticationSource.getAuthentication().getOperator();
        User agency = directWebAuthenticationSource.getAuthentication().getSubjectUser();

        if (agency.getRole() != RbacRole.AGENCY) {
            return false;
        }

        if (!rbacService.isOwner(operator.getUid(), agency.getUid())) {
            return false;
        }

        if (operator.getRole().anyOf(RbacRole.SUPER, RbacRole.MANAGER, RbacRole.AGENCY)) {
            if (operator.getRole() == RbacRole.AGENCY) {
                if (!operator.getClientId().equals(agency.getClientId())) {
                    // сюда не должны попадать после проверки isOwner, но паранойя сильнее
                    return false;
                }

                // Для агентств дополнительная проверка по типу представителя
                if (operator.getRepType() == RbacRepType.CHIEF || operator.getRepType() == RbacRepType.MAIN) {
                    // главным и основным представителям - можно работать с любым представителем агентства
                    return true;
                } else if (operator.getRepType() == RbacRepType.LIMITED) {
                    // ограниченным - только со своим логином
                    return operator.getUid().equals(agency.getUid());
                }
            } else {
                // суперпользователям и менеджерам - можно работать с любым доступным представителем агентства
                return true;
            }
        } else if (operator.getRole().anyOf(RbacRole.SUPERREADER, RbacRole.SUPPORT)) {
            return permission.isRead();
        }
        return false;
    }

    private static AgencyOfflineReportWebParams convertOfflineReportToWebOfflineReport(AgencyOfflineReport report) {
        return new AgencyOfflineReportWebParams()
                .withReportId(report.getReportId())
                .withBuiltAt(report.getScheduledAt())
                .withAgencyLogin(report.getArgs().getAgencyLogin())
                .withReportKind(report.getArgs().getReportKind())
                .withDateStart(report.getArgs().getDateFrom())
                .withDateEnd(report.getArgs().getDateTo())
                .withReportStatus(report.getReportState());
    }

    private WebErrorResponse noRightsResponse() {
        String message = translationService.translate(CommonDefectTranslations.INSTANCE.noRightsShort());
        return new WebErrorResponse(ErrorCodes.NO_RIGHTS, message);
    }
}
