package com.dji.sample.wayline.controller;

import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.wayline.model.dto.WaylineFileDTO;
import com.dji.sample.wayline.model.dto.WaylineFileTreeVO;
import com.dji.sample.wayline.model.param.WaylineSearchParam;
import com.dji.sample.wayline.model.param.WaylineUpdateParentParam;
import com.dji.sample.wayline.service.IWaylineFileService;
import com.dji.sdk.cloudapi.device.DeviceEnum;
import com.dji.sdk.cloudapi.wayline.*;
import com.dji.sdk.cloudapi.wayline.api.IHttpWaylineService;
import com.dji.sdk.common.HttpResultResponse;
import com.dji.sdk.common.PaginationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;

/**
 * @author sean
 * @version 0.3
 * @date 2021/12/22
 */
@RestController
public class WaylineFileController implements IHttpWaylineService {

    @Autowired
    private IWaylineFileService waylineFileService;

    /**
     * Delete the wayline file in the workspace according to the wayline id.
     * @param workspaceId
     * @param waylineId
     * @return
     */
    @DeleteMapping("${url.wayline.prefix}${url.wayline.version}/workspaces/{workspace_id}/waylines/{wayline_id}")
    public HttpResultResponse deleteWayline(@PathVariable(name = "workspace_id") String workspaceId,
                                            @PathVariable(name = "wayline_id") String waylineId) {
        boolean isDel = waylineFileService.deleteByWaylineId(workspaceId, waylineId);
        return isDel ? HttpResultResponse.success() : HttpResultResponse.error("Failed to delete wayline.");
    }

    /**
     * Import kmz wayline files.
     * @param file
     * @return
     */
    @PostMapping("${url.wayline.prefix}${url.wayline.version}/workspaces/{workspace_id}/waylines/file/upload")
    public HttpResultResponse importKmzFile(HttpServletRequest request, MultipartFile file) {
        if (Objects.isNull(file)) {
            return HttpResultResponse.error("No file received.");
        }
        CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
        String workspaceId = customClaim.getWorkspaceId();
        String creator = customClaim.getUsername();
        waylineFileService.importKmzFile(file, workspaceId, creator);
        return HttpResultResponse.success();
    }

    /**
     * Query the basic data of the wayline file according to the query conditions.
     * The query condition field in pilot is fixed.
     * @param request
     * @param workspaceId
     * @return
     */
    @Override
    public HttpResultResponse<PaginationData<GetWaylineListResponse>> getWaylineList(@Valid GetWaylineListRequest request, String workspaceId, HttpServletRequest req, HttpServletResponse rsp) {
        PaginationData<GetWaylineListResponse> data = waylineFileService.getWaylinesByParam(workspaceId, request);
        return HttpResultResponse.success(data);
    }

    /**
     * Query the download address of the file according to the wayline file id,
     * and redirect to this address directly for download.
     * @param workspaceId
     * @param waylineId
     * @param req
     * @param rsp
     */
    @Override
    public void getWaylineFileDownloadAddress(String workspaceId, String waylineId, HttpServletRequest req, HttpServletResponse rsp) {
        try {
            URL url = waylineFileService.getObjectUrl(workspaceId, waylineId);
            rsp.sendRedirect(url.toString());

        } catch (IOException | SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Checking whether the name already exists according to the wayline name must ensure the uniqueness of the wayline name.
     * This interface will be called when uploading waylines and must be available.
     * @param workspaceId
     * @param names
     * @return
     */
    @Override
    public HttpResultResponse<List<String>> getDuplicatedWaylineName(String workspaceId, @NotNull @Size(min = 1) List<String> names, HttpServletRequest req, HttpServletResponse rsp) {
        List<String> existNamesList = waylineFileService.getDuplicateNames(workspaceId, names);

        return HttpResultResponse.success(existNamesList);
    }

    /**
     * When the wayline file is uploaded to the storage server by pilot,
     * the basic information of the file is reported through this interface.
     * @param request
     * @param workspaceId
     * @return
     */
    @Override
    public HttpResultResponse fileUploadResultReport(String workspaceId, @Valid WaylineUploadCallbackRequest request, HttpServletRequest req, HttpServletResponse rsp) {
        CustomClaim customClaim = (CustomClaim)req.getAttribute(TOKEN_CLAIM);

        WaylineUploadCallbackMetadata metadata = request.getMetadata();

        WaylineFileDTO file = WaylineFileDTO.builder()
                .username(customClaim.getUsername())
                .objectKey(request.getObjectKey())
                .name(request.getName())
                .templateTypes(metadata.getTemplateTypes().stream().map(WaylineTypeEnum::getValue).collect(Collectors.toList()))
                .payloadModelKeys(metadata.getPayloadModelKeys().stream().map(DeviceEnum::getDevice).collect(Collectors.toList()))
                .droneModelKey(metadata.getDroneModelKey().getDevice())
                .build();

        int id = waylineFileService.saveWaylineFile(workspaceId, file);

        return id <= 0 ? HttpResultResponse.error() : HttpResultResponse.success();
    }

    /**
     * Favorite the wayline file according to the wayline file id.
     * @param workspaceId
     * @param ids   wayline file id
     * @return
     */
    @Override
    public HttpResultResponse batchFavoritesWayline(String workspaceId, @NotNull @Size(min = 1) List<String> ids, HttpServletRequest req, HttpServletResponse rsp) {
        boolean isMark = waylineFileService.markFavorite(workspaceId, ids, true);

        return isMark ? HttpResultResponse.success() : HttpResultResponse.error();
    }

    /**
     * Delete the favorites of this wayline file based on the wayline file id.
     * @param workspaceId
     * @param ids wayline file id
     * @return
     */
    @Override
    public HttpResultResponse batchUnfavoritesWayline(String workspaceId, @NotNull @Size(min = 1) List<String> ids, HttpServletRequest req, HttpServletResponse rsp) {
        boolean isMark = waylineFileService.markFavorite(workspaceId, ids, false);

        return isMark ? HttpResultResponse.success() : HttpResultResponse.error();
    }

    /**
     * Import kmz wayline files with parentId.
     * @param file
     * @return
     */
    @PostMapping("${url.wayline.prefix}${url.wayline.version}/workspaces/{workspace_id}/waylines/file/uploadWithParentId")
    public HttpResultResponse importKmzFileWithParentId(HttpServletRequest request, MultipartFile file,
                                                        @RequestParam(name = "parent_id", required = false) String parentId) {
        if (Objects.isNull(file)) {
            return HttpResultResponse.error("No file received.");
        }
        CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
        String workspaceId = customClaim.getWorkspaceId();
        String creator = customClaim.getUsername();
        waylineFileService.importKmzFile(file, workspaceId, creator, parentId);
        return HttpResultResponse.success();
    }

    /**
     * add new directory
     * @param workspaceId
     * @param waylineFileDTO
     * @return
     */
    @PostMapping(PREFIX + "/workspaces/{workspace_id}/addDirectory")
    public HttpResultResponse addDirectory(@PathVariable("workspace_id") String workspaceId,
                                           @RequestBody WaylineFileDTO waylineFileDTO) {
        Integer addDirId = waylineFileService.addDirectory(workspaceId, waylineFileDTO);

        return addDirId > 0 ? HttpResultResponse.success() : HttpResultResponse.error();
    }

    /**
     * get directory tree
     * @param workspaceId
     * @return
     */
    @GetMapping(PREFIX + "/workspaces/{workspace_id}/getDirectoryTree")
    public HttpResultResponse getDirectoryTree(@PathVariable("workspace_id") String workspaceId) {

        List<WaylineFileTreeVO> treeVOS = waylineFileService.getDirectoryTree(workspaceId);

        return HttpResultResponse.success(treeVOS);
    }

    /**
     * get directory tree
     * @param workspaceId
     * @return
     */
    @GetMapping(PREFIX + "/workspaces/{workspace_id}/getWaylineFileTree")
    public HttpResultResponse getWaylineFileTree(@PathVariable("workspace_id") String workspaceId,
                                                 @RequestParam(name = "waylineFileId") String id) {

        List<WaylineFileTreeVO> treeVOS = waylineFileService.getWaylineFileTree(workspaceId, id);

        return HttpResultResponse.success(treeVOS);
    }

    /**
     * get directory tree
     * @param workspaceId
     * @return
     */
    @PostMapping(PREFIX + "/workspaces/{workspace_id}/updateParent")
    public HttpResultResponse updateParent(@PathVariable("workspace_id") String workspaceId,
                                           @RequestBody WaylineFileDTO waylineFileDTO) {

        waylineFileService.updateParentId(waylineFileDTO.getWaylineId(), waylineFileDTO.getParentId());

        return HttpResultResponse.success();
    }

    /**
     * batch update parentId
     * @param workspaceId
     * @return
     */
    @PostMapping(PREFIX + "/workspaces/{workspace_id}/updateParentBatch")
    public HttpResultResponse updateParentBatch(@PathVariable("workspace_id") String workspaceId,
                                                @RequestBody WaylineUpdateParentParam waylineUpdateParentParam) {

        waylineFileService.updateParentIdBatch(waylineUpdateParentParam.getWaylineIds(), waylineUpdateParentParam.getParentId());

        return HttpResultResponse.success();
    }

    /**
     * get directory tree
     * @param workspaceId
     * @return
     */
    @DeleteMapping(PREFIX + "/workspaces/{workspace_id}/deleteDirectory")
    public HttpResultResponse deleteDirectory(@PathVariable("workspace_id") String workspaceId,
                                              @RequestParam(name = "waylineFileId") String id) {

        waylineFileService.deleteDirectory(workspaceId, id);

        return HttpResultResponse.success();
    }

    /**
     * Delete the wayline file in the workspace according to the wayline id.
     * @param workspaceId
     * @param ids
     * @param waylineIds
     * @return
     */
    @DeleteMapping(PREFIX + "/workspaces/{workspace_id}/waylines/deleteBtach")
    public HttpResultResponse deleteWaylineBatch(@PathVariable(name = "workspace_id") String workspaceId,
                                                 String ids,
                                                 String waylineIds) {
        List<String> idList = StringUtils.hasText(ids) ? Arrays.asList(ids.split(",")) : new ArrayList<>();
        List<String> waylineIdList = StringUtils.hasText(waylineIds) ? Arrays.asList(waylineIds.split(",")) : new ArrayList<>();
        boolean isDel = waylineFileService.deleteBatchByIdOrWaylineId(workspaceId, idList, waylineIdList);
        return isDel ? HttpResultResponse.success() : HttpResultResponse.error("Failed to delete wayline.");
    }

    /**
     * Query the basic data of the wayline file according to the query conditions.
     * The query condition field in pilot is fixed.
     * @param request
     * @param workspaceId
     * @return
     */
    @GetMapping(PREFIX + "/workspaces/{workspace_id}/getFileListByParentId")
    public HttpResultResponse<PaginationData<GetWaylineListResponse>> getFileListByParentId(
            WaylineSearchParam param,
            @Valid GetWaylineListRequest request,
            @PathVariable("workspace_id") String workspaceId,
            @RequestParam(name = "parent_id", defaultValue = "0") String parentId,
            HttpServletRequest req,
            HttpServletResponse rsp) {
        PaginationData<GetWaylineListResponse> data = waylineFileService.getWaylinesByParam(param, workspaceId, request);
        return HttpResultResponse.success(data);
    }

    /**
     * Update wayline file information (name, drone_model_key, payload_model_keys, template_types)
     * @param workspaceId
     * @param waylineId
     * @param waylineFileDTO
     * @return
     */
    @PutMapping(PREFIX + "/workspaces/{workspace_id}/waylines/{wayline_id}")
    public HttpResultResponse updateWaylineFile(@PathVariable("workspace_id") String workspaceId,
                                                @PathVariable("wayline_id") String waylineId,
                                                @RequestBody WaylineFileDTO waylineFileDTO) {
        boolean success = waylineFileService.updateWaylineFile(workspaceId, waylineId, waylineFileDTO);
        return success ? HttpResultResponse.success() : HttpResultResponse.error("Failed to update wayline file.");
    }

}
