package ru.yandex.canvas.controllers.video;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.PositiveOrZero;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
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.controllers.OffsetTotalItemsResponse;
import ru.yandex.canvas.controllers.video.wrappers.AudioFileWrapper;
import ru.yandex.canvas.controllers.video.wrappers.FileWrapper;
import ru.yandex.canvas.controllers.video.wrappers.VideoFileWrapper;
import ru.yandex.canvas.model.video.files.AudioSource;
import ru.yandex.canvas.model.video.files.Movie;
import ru.yandex.canvas.model.video.files.VideoSource;
import ru.yandex.canvas.service.video.VideoPresetsService;
import ru.yandex.canvas.service.video.files.CategoriesRecord;
import ru.yandex.canvas.service.video.files.StockMoviesService;
import ru.yandex.canvas.service.video.files.VideoCategoriesService;

@RestController
@RequestMapping(path = "/video/files")
public class VideoStockFilesSearchController {
    private StockMoviesService stockMoviesService;
    private VideoCategoriesService videoCategoriesService;
    private final VideoPresetsService videoPresetsService;

    public VideoStockFilesSearchController(StockMoviesService stockMoviesService,
                                           VideoCategoriesService videoCategoriesService,
                                           VideoPresetsService videoPresetsService) {
        this.stockMoviesService = stockMoviesService;
        this.videoCategoriesService = videoCategoriesService;
        this.videoPresetsService = videoPresetsService;
    }

    @GetMapping(path = "/stock")
    public ResponseEntity<OffsetTotalItemsResponse<FileWrapper>> getStockFiles(
            @RequestParam(value = "limit", defaultValue = "50") @Valid @PositiveOrZero Integer limit,
            @RequestParam(value = "offset", defaultValue = "0") @Valid @PositiveOrZero Integer offset,
            @RequestParam("type") @Valid @Pattern(regexp = "^(?:video|audio|constructor_audio)$") String type,
            @RequestParam(value = "part", required = false) String nameSubString,
            @RequestParam(value = "category_id", required = false) List<String> categories,
            @RequestParam(value = "group_id", required = false) String groupIds,
            @RequestParam(value = "preset_id", required = false) Long presetId,
            @RequestParam(value = "semantic_search", defaultValue = "") String semanticSearch,
            HttpServletRequest request) {
        if ("audio".equals(type)) {
            return searchAudioStockFiles(nameSubString, limit, offset);
        }

        if ("constructor_audio".equals(type)) {
            return searchConstructorAudioStockFiles(nameSubString, limit, offset);
        }

        List<Integer> groups;

        if (groupIds == null) {
            groups = null;
        } else {
            groups = Arrays.stream(groupIds.split(",\\s*")).map(e -> Integer.parseInt(e) - 1)
                    .collect(Collectors.toList());
        }

        Map<Long, CategoriesRecord> categoriesRecordMap = videoCategoriesService.getCategoriesMap();

        if (categories != null) {
            categories = categories.stream()
                    .map(e -> categoriesRecordMap.get(Long.parseLong(e)))
                    .filter(Objects::nonNull)
                    .flatMap(e -> e.getSubCategories().stream())
                    .distinct()
                    .map(e -> e + "")
                    .collect(Collectors.toList());
        }

        return searchVideoStockFile(categories, groups, presetId, nameSubString, semanticSearch, limit, offset);
    }

    ResponseEntity<OffsetTotalItemsResponse<FileWrapper>> searchAudioStockFiles(
            String nameSubString, int limit,
            int offset) {
        StockMoviesService.SearchResult<AudioSource>
                audioSources = stockMoviesService.searchAudio(nameSubString, limit, offset);

        List<FileWrapper> records = audioSources.getResult()
                .stream()
                .filter(e -> e.getId() != null)
                .map(AudioFileWrapper::new)
                .collect(Collectors.toList());

        return ResponseEntity.ok(
                new OffsetTotalItemsResponse<>(
                        records,
                        offset,
                        audioSources.getTotal()
                )
        );
    }

    ResponseEntity<OffsetTotalItemsResponse<FileWrapper>> searchConstructorAudioStockFiles(
            String nameSubString, int limit,
            int offset) {
        StockMoviesService.SearchResult<AudioSource>
                audioSources = stockMoviesService.searchConstructorAudio(nameSubString, limit, offset);

        List<FileWrapper> records = audioSources.getResult()
                .stream()
                .map(AudioFileWrapper::new)
                .collect(Collectors.toList());

        return ResponseEntity.ok(
                new OffsetTotalItemsResponse<>(
                        records,
                        offset,
                        audioSources.getTotal()
                )
        );
    }

    ResponseEntity<OffsetTotalItemsResponse<FileWrapper>> searchVideoStockFile(
            List<String> categories, List<Integer> groups, Long presetId,
            String nameSubString, String semanticSearch, int limit, int offset) {
        //TODO categories aren't null
        StockMoviesService.SearchResult<VideoSource> videoSources =
                stockMoviesService.searchVideo(categories, groups, nameSubString,
                        videoPresetsService.getPresetSafe(presetId),
                        limit, offset, semanticSearch);

        List<Movie> movies = videoSources.getResult()
                .stream().map(e -> stockMoviesService.getFileByIds(e.getId(), null))
                .collect(Collectors.toList());

        return ResponseEntity.ok(
                new OffsetTotalItemsResponse<>(
                        movies.stream()
                                .map(VideoFileWrapper::new)
                                .collect(Collectors.toList()),
                        offset,
                        videoSources.getTotal()
                )
        );
    }

}
