package ru.yandex.direct.web.entity.admin;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import one.util.streamex.StreamEx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import ru.yandex.direct.common.spring.TestingComponent;
import ru.yandex.direct.rbac.RbacRole;
import ru.yandex.direct.validation.presentation.DefectPresentationRegistry;
import ru.yandex.direct.validation.result.DefectId;
import ru.yandex.direct.web.annotations.AllowedOperatorRoles;
import ru.yandex.direct.web.annotations.AllowedSubjectRoles;
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.validation.kernel.TranslatableWebDefect;

@ParametersAreNonnullByDefault
@RequestMapping(value = "defect",
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@Api(tags = "defect")
@TestingComponent
public class DefectController {
    private final DefectPresentationRegistry<TranslatableWebDefect> defectPresentationRegistry;

    @Autowired
    public DefectController(DefectPresentationRegistry<TranslatableWebDefect> defectPresentationRegistry) {
        this.defectPresentationRegistry = defectPresentationRegistry;
    }

    @ApiOperation(
            value = "Получение списка зарегистрированных дефектов. "
                    + "Ручка для служебного ручного использования при разработке. "
                    + "Будут пользоваться разработчики фронта. "
                    + "Доступна только суперам и супер-ридерам.",
            httpMethod = "GET",
            nickname = "list"
    )
    @ApiResponses(
            {
                    @ApiResponse(code = 403, message = "Permission denied", response = WebErrorResponse.class),
                    @ApiResponse(code = 200, message = "Ok", response = DefectListResponse.class)
            }
    )
    @RequestMapping(path = "list",
            method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    @AllowedOperatorRoles({RbacRole.SUPER, RbacRole.SUPERREADER})
    @AllowedSubjectRoles({RbacRole.SUPER, RbacRole.SUPERREADER})
    public WebResponse getDefectList() {
        @SuppressWarnings("unchecked")
        List<DefectDescription> descriptions = StreamEx.of(defectPresentationRegistry.getRegisteredDefectIds())
                .map(this::toDescription)
                .toList();
        return new DefectListResponse().withDefects(descriptions);
    }

    private <T> DefectDescription toDescription(DefectId<T> defectId) {
        //получение класса параметров через reflection, предполагается, что у DefectId 1н generic параметр
        ParameterizedType type = (ParameterizedType) defectId.getClass()
                .getGenericInterfaces()[0];
        Type paramType = type.getActualTypeArguments()[0];
        Class paramsClass = (Class) (
                paramType instanceof ParameterizedType
                        ? ((ParameterizedType) paramType).getRawType()
                        : paramType
        );
        DefectDescription description = new DefectDescription();
        description.setCode(defectId.getCode());
        description.setParamsClass(paramsClass.getCanonicalName());
        try {
            description.setParams(paramsClass.newInstance());
        } catch (Exception e) {
            //если не можем создать экземпляр параметров, то считаем, что праметров нет
            description.setParams(null);
        }
        return description;
    }

    public static class DefectListResponse extends WebSuccessResponse {
        @JsonProperty("defects")
        private List<DefectDescription> defects;

        public List<DefectDescription> getDefects() {
            return defects;
        }

        public void setDefects(List<DefectDescription> defects) {
            this.defects = defects;
        }

        public DefectListResponse withDefects(List<DefectDescription> defects) {
            this.defects = defects;
            return this;
        }
    }

    static class DefectDescription {
        private String code;
        @JsonProperty("params_class")
        private String paramsClass;
        private Object params;

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public Object getParams() {
            return params;
        }

        public void setParams(@Nullable Object params) {
            this.params = params;
        }

        public String getParamsClass() {
            return paramsClass;
        }

        public void setParamsClass(String paramsClass) {
            this.paramsClass = paramsClass;
        }
    }
}
