package org.dromara.member.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaIgnore;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.member.domain.bo.AppFileBo;
import org.dromara.member.domain.vo.AppFileVo;
import org.dromara.member.service.AppFileService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;
import java.util.List;

/**
 * App端文件信息Controller
 *
 * @author pengles
 */
@Tag(name = "App端文件管理", description = "App端文件信息管理接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/file")
public class AppFileController extends BaseController {

    private final AppFileService appFileService;

    /**
     * 查询App端文件信息列表
     */
    @Operation(summary = "查询文件列表", description = "分页查询当前用户的文件信息列表")
    @SaCheckLogin
    @GetMapping("/list")
    public TableDataInfo<AppFileVo> list(
            @Parameter(description = "文件查询条件") @Valid AppFileBo bo,
            @Parameter(description = "分页参数") @Valid PageQuery pageQuery) {
        // 只能查询自己的文件
        bo.setUserId(LoginHelper.getUserId());
        return appFileService.queryPageList(bo, pageQuery);
    }

    /**
     * 获取App端文件信息详细信息
     */
    @Operation(summary = "获取文件详情", description = "根据文件ID获取文件详细信息")
    @SaCheckLogin
    @GetMapping("/{fileId}")
    public R<AppFileVo> getInfo(
            @Parameter(description = "文件ID", required = true)
            @PathVariable @NotNull(message = "文件ID不能为空") @Positive(message = "文件ID必须为正数") Long fileId) {
        AppFileVo fileVo = appFileService.queryById(fileId);
        if (fileVo == null) {
            return R.fail("文件不存在");
        }
        // 检查权限
        if (!fileVo.getUserId().equals(LoginHelper.getUserId()) && !"1".equals(fileVo.getIsPublic())) {
            return R.fail("无权限访问该文件");
        }
        return R.ok(fileVo);
    }

    /**
     * 新增App端文件信息
     */
    @Operation(summary = "新增文件信息", description = "添加新的文件信息记录")
    @SaCheckLogin
    @Log(title = "App端文件信息", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(
            @Parameter(description = "文件信息", required = true)
            @Validated(AddGroup.class) @RequestBody AppFileBo bo) {
        bo.setUserId(LoginHelper.getUserId());
        return toAjax(appFileService.insertByBo(bo));
    }

    /**
     * 修改App端文件信息
     */
    @Operation(summary = "修改文件信息", description = "更新文件信息记录")
    @SaCheckLogin
    @Log(title = "App端文件信息", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(
            @Parameter(description = "文件信息", required = true)
            @Validated(EditGroup.class) @RequestBody AppFileBo bo) {
        // 检查权限
        AppFileVo existFile = appFileService.queryById(bo.getFileId());
        if (existFile == null) {
            return R.fail("文件不存在");
        }
        if (!existFile.getUserId().equals(LoginHelper.getUserId())) {
            return R.fail("无权限修改该文件");
        }
        return toAjax(appFileService.updateByBo(bo));
    }

    /**
     * 删除App端文件信息
     */
    @Operation(summary = "删除文件", description = "批量删除文件信息")
    @SaCheckLogin
    @Log(title = "App端文件信息", businessType = BusinessType.DELETE)
    @DeleteMapping("/{fileIds}")
    public R<Void> remove(
            @Parameter(description = "文件ID列表", required = true)
            @PathVariable @NotEmpty(message = "文件ID列表不能为空") Long[] fileIds) {
        return toAjax(appFileService.batchDeleteUserFiles(Arrays.asList(fileIds), LoginHelper.getUserId()));
    }

    /**
     * 上传文件
     */
    @Operation(summary = "上传文件", description = "上传单个文件")
    @SaCheckLogin
    @Log(title = "文件上传", businessType = BusinessType.INSERT)
    @PostMapping("/upload")
    public R<AppFileVo> uploadFile(
            @Parameter(description = "上传的文件", required = true)
            @RequestParam("file") @NotNull(message = "上传文件不能为空") MultipartFile file,
            @Parameter(description = "文件分类")
            @RequestParam(value = "fileCategory", required = false) @Min(value = 0, message = "文件分类不能为负数") Integer fileCategory) {
        AppFileVo result = appFileService.uploadFile(file, LoginHelper.getUserId(), fileCategory);
        return R.ok(result);
    }

    /**
     * 上传头像
     */
    @Operation(summary = "上传头像", description = "上传用户头像文件")
    @SaCheckLogin
    @Log(title = "头像上传", businessType = BusinessType.INSERT)
    @PostMapping("/upload/avatar")
    public R<AppFileVo> uploadAvatar(
            @Parameter(description = "头像文件", required = true)
            @RequestParam("file") @NotNull(message = "头像文件不能为空") MultipartFile file) {
        AppFileVo result = appFileService.uploadAvatar(file, LoginHelper.getUserId());
        return R.ok(result);
    }

    /**
     * 批量上传文件
     */
    @Operation(summary = "批量上传文件", description = "批量上传多个文件")
    @SaCheckLogin
    @Log(title = "批量文件上传", businessType = BusinessType.INSERT)
    @PostMapping("/upload/batch")
    public R<List<AppFileVo>> batchUploadFiles(
            @Parameter(description = "上传的文件列表", required = true)
            @RequestParam("files") @NotEmpty(message = "上传文件列表不能为空") MultipartFile[] files,
            @Parameter(description = "文件分类")
            @RequestParam(value = "fileCategory", required = false) @Min(value = 0, message = "文件分类不能为负数") Integer fileCategory) {
        List<AppFileVo> result = appFileService.batchUploadFiles(files, LoginHelper.getUserId(), fileCategory);
        return R.ok(result);
    }

    /**
     * 根据文件分类查询文件列表
     */
    @Operation(summary = "根据分类获取文件", description = "获取指定分类的文件列表")
    @SaCheckLogin
    @GetMapping("/category/{fileCategory}")
    public R<List<AppFileVo>> getFilesByCategory(
            @Parameter(description = "文件分类", required = true)
            @PathVariable @NotNull(message = "文件分类不能为空") @Min(value = 0, message = "文件分类不能为负数") Integer fileCategory) {
        List<AppFileVo> result = appFileService.queryByUserId(LoginHelper.getUserId(), fileCategory);
        return R.ok(result);
    }

    /**
     * 下载文件
     */
    @Operation(summary = "下载文件", description = "根据文件ID下载文件")
    @SaCheckLogin
    @Log(title = "文件下载", businessType = BusinessType.OTHER)
    @GetMapping("/download/{fileId}")
    public R<AppFileVo> downloadFile(
            @Parameter(description = "文件ID", required = true)
            @PathVariable @NotNull(message = "文件ID不能为空") @Positive(message = "文件ID必须为正数") Long fileId) {
        AppFileVo result = appFileService.downloadFile(fileId, LoginHelper.getUserId());
        return R.ok(result);
    }

    /**
     * 更新文件状态
     */
    @Operation(summary = "更新文件状态", description = "更新文件的状态信息")
    @SaCheckLogin
    @Log(title = "更新文件状态", businessType = BusinessType.UPDATE)
    @PutMapping("/status/{fileId}")
    public R<Void> updateFileStatus(
            @Parameter(description = "文件ID", required = true)
            @PathVariable @NotNull(message = "文件ID不能为空") @Positive(message = "文件ID必须为正数") Long fileId,
            @Parameter(description = "文件状态(0-正常,1-禁用)", required = true)
            @RequestParam @NotBlank(message = "文件状态不能为空") @Pattern(regexp = "^(0|1)$", message = "文件状态只能为0或1") String status) {
        return toAjax(appFileService.updateFileStatus(fileId, status, LoginHelper.getUserId()));
    }

    /**
     * 更新文件公开状态
     */
    @Operation(summary = "更新文件公开状态", description = "设置文件是否公开")
    @SaCheckLogin
    @Log(title = "更新文件公开状态", businessType = BusinessType.UPDATE)
    @PutMapping("/public/{fileId}")
    public R<Void> updateFilePublicStatus(
            @Parameter(description = "文件ID", required = true)
            @PathVariable @NotNull(message = "文件ID不能为空") @Positive(message = "文件ID必须为正数") Long fileId,
            @Parameter(description = "是否公开(0-私有,1-公开)", required = true)
            @RequestParam @NotBlank(message = "公开状态不能为空") @Pattern(regexp = "^(0|1)$", message = "公开状态只能为0或1") String isPublic) {
        return toAjax(appFileService.updateFilePublicStatus(fileId, isPublic, LoginHelper.getUserId()));
    }

    /**
     * 获取用户文件统计信息
     */
    @Operation(summary = "获取文件统计", description = "获取当前用户的文件统计信息")
    @SaCheckLogin
    @GetMapping("/statistics")
    public R<AppFileVo> getUserFileStatistics() {
        AppFileVo result = appFileService.getUserFileStatistics(LoginHelper.getUserId());
        return R.ok(result);
    }

    /**
     * 检查文件是否存在
     */
    @Operation(summary = "检查文件是否存在", description = "根据MD5值检查文件是否已存在")
    @SaCheckLogin
    @GetMapping("/check/{fileMd5}")
    public R<AppFileVo> checkFileExists(
            @Parameter(description = "文件MD5值", required = true)
            @PathVariable @NotBlank(message = "文件MD5不能为空") @Size(min = 32, max = 32, message = "MD5长度必须为32位") String fileMd5) {
        AppFileVo result = appFileService.checkFileExists(fileMd5, LoginHelper.getUserId());
        return R.ok(result);
    }

    /**
     * 查询公开文件列表
     */
    @Operation(summary = "查询公开文件列表", description = "获取指定分类的公开文件列表")
    @SaIgnore
    @GetMapping("/public/category/{fileCategory}")
    public R<List<AppFileVo>> getPublicFilesByCategory(
            @Parameter(description = "文件分类", required = true)
            @PathVariable @NotNull(message = "文件分类不能为空") @Min(value = 0, message = "文件分类不能为负数") Integer fileCategory) {
        List<AppFileVo> result = appFileService.queryByCategory(fileCategory);
        return R.ok(result);
    }

}