/*
 * Copyright 2024-2026 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.cloud.ai.studio.admin.builder.controller;

import com.alibaba.cloud.ai.studio.runtime.exception.BizException;
import com.alibaba.cloud.ai.studio.runtime.enums.ErrorCode;
import com.alibaba.cloud.ai.studio.runtime.domain.RequestContext;
import com.alibaba.cloud.ai.studio.runtime.domain.Result;
import com.alibaba.cloud.ai.studio.runtime.domain.file.UploadPolicy;
import com.alibaba.cloud.ai.studio.runtime.domain.file.WebUploadPolicy;
import com.alibaba.cloud.ai.studio.runtime.domain.file.WebUploadRequest;
import com.alibaba.cloud.ai.studio.core.context.RequestContextHolder;
import com.alibaba.cloud.ai.studio.core.base.manager.FileManager;
import com.alibaba.cloud.ai.studio.core.base.manager.OssManager;
import com.alibaba.cloud.ai.studio.core.utils.io.FileUtils;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Controller for handling file operations including upload and download
 */
@RestController
@Tag(name = "file")
@RequestMapping("/console/v1/files")
@RequiredArgsConstructor
public class FileController {

	/** File manager for handling file operations */
	private final FileManager fileManager;

	/** Oss manager */
	private final OssManager ossManager;

	/**
	 * Upload multiple files and return their upload policies
	 * @param files Array of files to upload
	 * @param category Category of the files
	 * @return List of upload policies for the uploaded files
	 */
	@PostMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Result<List<UploadPolicy>> uploadDocument(@RequestPart("files") MultipartFile[] files,
			@RequestPart("category") String category) {
		RequestContext context = RequestContextHolder.getRequestContext();

		if (Objects.isNull(category)) {
			throw new BizException(ErrorCode.MISSING_PARAMS.toError("category"));
		}

		if (ArrayUtils.isEmpty(files)) {
			throw new BizException(ErrorCode.MISSING_PARAMS.toError("files"));
		}

		List<UploadPolicy> policies = new ArrayList<>();
		for (MultipartFile file : files) {
			String path = fileManager.saveFile(file, category, context);
			String ext = FilenameUtils.getExtension(file.getOriginalFilename());
			ext = StringUtils.lowerCase(ext);
			UploadPolicy uploadPolicy = UploadPolicy.builder()
				.path(path)
				.name(file.getOriginalFilename())
				.extension(ext)
				.contentType(file.getContentType())
				.size(file.getSize())
				.build();

			policies.add(uploadPolicy);
		}

		return Result.success(context.getRequestId(), policies);
	}

	/**
	 * Download a file from the server
	 * @param filePath Path of the file to download
	 * @param preview Whether to preview the file in browser
	 * @param response HTTP response object
	 * @throws IOException If file operations fail
	 */
	@GetMapping("/download")
	public void downloadFile(@RequestParam(value = "path") String filePath,
			@RequestParam(value = "preview", defaultValue = "false") boolean preview, HttpServletResponse response)
			throws IOException {
		// TODO check permission, there might be a file management instead?
		if (Objects.isNull(filePath)) {
			throw new BizException(ErrorCode.MISSING_PARAMS.toError("path"));
		}

		Resource resource = fileManager.loadFile(filePath);
		File file = resource.getFile();

		String filename = UriUtils.encode(file.getName(), StandardCharsets.UTF_8);
		if (preview) {
			response.setContentType(MediaType.parseMediaType(FileUtils.getContentType(filename)).toString());
			response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
		}
		else {
			response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
			response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
		}

		response.setContentLengthLong(file.length());
		try (InputStream inputStream = resource.getInputStream();
				OutputStream outputStream = response.getOutputStream()) {
			byte[] buffer = new byte[4096];
			int bytesRead;
			while ((bytesRead = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, bytesRead);
			}
			outputStream.flush();
		}
	}

	/**
	 * get multiple files upload policies for oss
	 * @param request upload request
	 * @return List of upload policies for the uploaded files
	 */
	@PostMapping(value = "upload-policies")
	public Result<List<WebUploadPolicy>> getUploadPolicies(@RequestBody WebUploadRequest request) {
		RequestContext context = RequestContextHolder.getRequestContext();

		if (Objects.isNull(request.getCategory())) {
			throw new BizException(ErrorCode.MISSING_PARAMS.toError("category"));
		}

		if (CollectionUtils.isEmpty(request.getFiles())) {
			throw new BizException(ErrorCode.MISSING_PARAMS.toError("files"));
		}

		List<WebUploadPolicy> policies = new ArrayList<>();
		for (WebUploadRequest.FileMeta fileMeta : request.getFiles()) {
			WebUploadPolicy policy = ossManager.getWebUploadPolicy(context.getAccountId(), request.getCategory(),
					fileMeta.getName());
			policies.add(policy);
		}

		return Result.success(context.getRequestId(), policies);
	}

	/**
	 * get preview url
	 * @param filePath Path of the file to download
	 */
	@GetMapping("/get-preview-url")
	public Result<String> getPreviewUrl(@RequestParam(value = "path") String filePath) {
		// TODO check permission, there might be a file management instead?
		RequestContext context = RequestContextHolder.getRequestContext();
		if (Objects.isNull(filePath)) {
			throw new BizException(ErrorCode.MISSING_PARAMS.toError("path"));
		}

		String url = ossManager.generateURL(filePath);
		return Result.success(context.getRequestId(), url);
	}

}
