package ru.yandex.canvas.controllers.video;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import ru.yandex.canvas.configs.auth.AuthorizeBy;
import ru.yandex.canvas.exceptions.InternalServerError;
import ru.yandex.canvas.model.direct.CreativeInfo;
import ru.yandex.canvas.model.direct.CreativeUploadData;
import ru.yandex.canvas.model.direct.DirectUploadResult;
import ru.yandex.canvas.model.direct.Privileges;
import ru.yandex.canvas.model.direct.UcCreativeCanvasData;
import ru.yandex.canvas.model.direct.UcCreativeInfo;
import ru.yandex.canvas.model.video.Addition;
import ru.yandex.canvas.service.AuthService;
import ru.yandex.canvas.service.direct.VideoAdditionDirectUploadHelper;
import ru.yandex.canvas.service.video.CreativesGenerationService;
import ru.yandex.canvas.service.video.VideoCreativesService;
import ru.yandex.canvas.service.video.presets.PresetTag;

import static ru.yandex.canvas.configs.auth.AuthorizeBy.AuthType.DIRECT_TOKEN;
import static ru.yandex.direct.utils.FunctionalUtils.mapList;

@RestController
@RequestMapping(path = "/video/direct")
@Validated
public class VideoDirectController {
    private AuthService authService;
    private VideoCreativesService videoCreativesService;
    private CreativesGenerationService creativesGenerationService;
    private final VideoAdditionDirectUploadHelper videoAdditionDirectUploadHelper;

    public VideoDirectController(AuthService authService,
                                 VideoCreativesService videoCreativesService,
                                 CreativesGenerationService creativesGenerationService,
                                 VideoAdditionDirectUploadHelper videoAdditionDirectUploadHelper) {
        this.authService = authService;
        this.videoCreativesService = videoCreativesService;
        this.creativesGenerationService = creativesGenerationService;
        this.videoAdditionDirectUploadHelper = videoAdditionDirectUploadHelper;
    }

    //TODO copy-paste from html5directcontroller..
    @AuthorizeBy({DIRECT_TOKEN})
    @GetMapping(path = "creatives")
    public List<CreativeInfo> getCreatives(
            @RequestParam("client_id") Long clientId,
            @RequestParam(name = "ids")
            @Valid @Pattern(regexp = "(\\d+\\s*,\\s*)*\\s*\\d+\\s*") String ids
    ) {
        List<Long> parsedIds = Stream.of(ids.split(",\\s*"))
                .map(Long::parseLong)
                .collect(Collectors.toList());

        Map<Long, CreativeUploadData> creatives = videoCreativesService.getCreatives(parsedIds, clientId);

        List<CreativeInfo> answer = new ArrayList<>();

        for (Long id : parsedIds) {
            CreativeInfo creativeInfo = new CreativeInfo().setCreativeId(id);

            if (creatives.containsKey(id)) {
                creativeInfo
                        .setOk(true)
                        .setMessage(null)
                        .setCreative(creatives.get(id));
            } else {
                creativeInfo.setOk(false).setMessage("Not found");
            }

            answer.add(creativeInfo);
        }

        return answer;
    }

    /**
     * Контроллер по получению данных для отображения видеокреатива в uc, uac, оно же Лёгкий кабинет
     */
    @AuthorizeBy({DIRECT_TOKEN})
    @GetMapping(path = "ucCreativesData")
    public List<UcCreativeInfo> getUcCreatives(
            @RequestParam("client_id") Long clientId,
            @RequestParam(name = "ids")
            @Valid @Pattern(regexp = "(\\d+\\s*,\\s*)*\\s*\\d+\\s*") String ids
    ) {
        List<Long> parsedIds = Stream.of(ids.split(",\\s*"))
                .map(Long::parseLong)
                .collect(Collectors.toList());
        parsedIds.stream().map(String::valueOf).collect(Collectors.joining(","));
        Map<Long, UcCreativeCanvasData> creatives = videoCreativesService.getUcCreatives(parsedIds, clientId);

        List<UcCreativeInfo> answer = new ArrayList<>();

        for (Long id : parsedIds) {
            UcCreativeInfo creativeInfo = new UcCreativeInfo().setCreativeId(id);

            if (creatives.containsKey(id)) {
                creativeInfo
                        .setOk(true)
                        .setMessage(null)
                        .setCreative(creatives.get(id));
            } else {
                creativeInfo.setOk(false).setMessage("Not found");
            }

            answer.add(creativeInfo);
        }

        return answer;
    }

    public static class Request {
        public static class IdRec {
            String id;

            public String getId() {
                return id;
            }

            public IdRec setId(String id) {
                this.id = id;
                return this;
            }
        }

        List<IdRec> additions;

        public List<IdRec> getAdditions() {
            return additions;
        }

        public Request setAdditions(
                List<IdRec> additions) {
            this.additions = additions;
            return this;
        }
    }

    @PostMapping(path = "additions")
    public DirectUploadResult uploadVideo(
            @Valid @RequestBody Request uploadRequest,
            @RequestParam("client_id") Long clientId
    ) {
        authService.requirePermission(Privileges.Permission.CREATIVE_CREATE);

        List<String> ids = uploadRequest.additions.stream().map(Request.IdRec::getId).collect(Collectors.toList());

        return videoCreativesService.uploadToDirect(ids, authService.getUserId(), clientId);
    }

    public static class GenerateRequest {
        @NotNull
        @JsonProperty("client_id")
        private Long clientId;

        @Valid
        @NotEmpty
        @JsonProperty("conditions")
        private List<GenerateCondition> conditions;

        public Long getClientId() {
            return clientId;
        }

        public GenerateRequest setClientId(Long clientId) {
            this.clientId = clientId;
            return this;
        }

        public static class GenerateCondition {
            @JsonProperty("category_ids")
            private List<String> categories;

            @NotNull
            @JsonProperty("count")
            private Integer count;

            @Pattern(regexp = "^(?:en_US|ru_RU|tr_TR|uk_UA)$")
            @JsonProperty("locale")
            private String locale;

            @NotEmpty
            @Pattern(regexp = "^videoAddition$")
            @JsonProperty("creative_type")
            private String creativeType;

            @Pattern(regexp = "^(?:text|mobile_content)$")
            @JsonProperty("banner_type")
            private String bannerType;

            public List<String> getCategories() {
                return categories;
            }

            public void setCategories(List<String> categories) {
                this.categories = categories;
            }

            public Integer getCount() {
                return count;
            }

            public void setCount(Integer count) {
                this.count = count;
            }

            public String getLocale() {
                return locale == null ? "en_US" : locale;
            }

            public void setLocale(String locale) {
                this.locale = locale;
            }

            public String getCreativeType() {
                return creativeType;
            }

            public void setCreativeType(String creativeType) {
                this.creativeType = creativeType;
            }

            public String getBannerType() {
                return bannerType == null ? "text" : bannerType;
            }

            public void setBannerType(String bannerType) {
                this.bannerType = bannerType;
            }
        }
    }

    @AuthorizeBy({DIRECT_TOKEN})
    @PostMapping(path = "generate-creatives")
    public ResponseEntity<List<List<CreativeUploadData>>> generateCreatives(
            @RequestHeader(value = "_Random_Seed", required = false) String randomSeed,
            @RequestBody @Valid GenerateRequest request
    ) {

        final boolean doShuffle = randomSeed == null || randomSeed.isEmpty();

        List<CreativesGenerationService.GenerateCondition> conditions =

                request.conditions.stream().map(e -> {
                    PresetTag tag = "text".equals(e.getBannerType()) ? PresetTag.COMMON : PresetTag.MOBILE_CONTENT;

                    return new CreativesGenerationService.GenerateCondition(tag,
                            e.getCategories() != null && e.getCategories().size() == 0 ? null : e.getCategories(),
                            Collections.singletonList(e.getLocale()), e.getCount(), doShuffle
                    );

                }).collect(Collectors.toList());

        List<List<Addition>> additions;

        try {
            additions = creativesGenerationService.generateAdditions(conditions, request.clientId);
        } catch (IOException | URISyntaxException e) {
            throw new InternalServerError(e);
        }

        List<List<CreativeUploadData>> creativeData = mapList(
                additions,
                e -> mapList(e, v -> videoAdditionDirectUploadHelper.toCreativeUploadData(v, request.clientId))
        );

        return ResponseEntity.ok(creativeData);
    }

}
