package architecture.web.spring.web.controller.data.secure.mgmt.resources;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import architecture.ee.exception.NotFoundException;
import architecture.user.User;
import architecture.user.util.SecurityHelper;
import architecture.web.attachments.Attachment;
import architecture.web.attachments.AttachmentService;
import architecture.web.attachments.DefaultAttachment;
import architecture.web.attachments.dao.jpa.LocalAttachment;
import architecture.web.model.Models;
import architecture.web.share.SharedLinkService;
import architecture.web.spring.web.controller.data.upload.AbstractTusUploadDataController;
import architecture.web.spring.web.controller.data.upload.TusFileUploadStatus;
import architecture.web.spring.web.controller.data.upload.TusHttpHeader;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping(TusUploadDataController.TUS_REQUEST_URL)
@Tag(name = "mgmt files", description = "File Management APIs")
@RestController("controllers:mgmt:tus-upload-data-controller")
public class TusUploadDataController extends AbstractTusUploadDataController {

    public static final String TUS_REQUEST_URL = "/data/secure/mgmt/resources/files";

    final AttachmentService attachmentService;
    
    final SharedLinkService sharedLinkService;
    
    TusUploadDataController(
        @Qualifier(AttachmentService.SERVICE_NAME) AttachmentService attachmentService,
        @Autowired(required = false) @Qualifier(SharedLinkService.SERVICE_NAME) SharedLinkService sharedLinkService ) {
        this.attachmentService = attachmentService;
        this.sharedLinkService = sharedLinkService;
    }

    protected AttachmentService getAttachmentService() {
        return attachmentService;
    }

    protected String getUrlPrefix() {
        return TUS_REQUEST_URL;
    }

    protected void doUploadComplete(File file, TusFileUploadStatus status) throws NotFoundException, IOException {
        String contentType = Optional.ofNullable(status.getMeta().get("filetype"))
                .map(Object::toString)
                .orElse("application/octet-stream"); // 기본값을 application/octet-stream 설정
        long attachmentId = Optional.ofNullable(status.getMeta().get("attachmentId"))
                .map(Object::toString)
                .map(Long::parseLong)
                .orElse(0L); // 기본값을 0 설정
        int objectType = Optional.ofNullable(status.getMeta().get("objectType"))
                .map(Object::toString)
                .map(Integer::parseInt)
                .orElse(0); // 기본값을 0 설정
        long objectId = Optional.ofNullable(status.getMeta().get("objectId"))
                .map(Object::toString)
                .map(Long::parseLong)
                .orElse(0L); // 기본값을 0 설정
        boolean link = Optional.ofNullable(status.getMeta().get("link"))
                .map(Object::toString)
                .map(Boolean::parseBoolean)
                .orElse(false); // 기본값을 false 설정

        FileInputStream inputStream = new FileInputStream(file);
        Attachment attachment;     
        boolean isUpdate = attachmentId > 0 ? true : false;   
        if (isUpdate) {
            attachment = attachmentService.getAttachment(attachmentId);
            attachment.setContentType(contentType);
            attachment.setSize((int)status.getFileSize() );
            attachment.setInputStream(inputStream);
            attachment.setName(status.getFileName());
            setObjectTypeAndObjectId(attachment, objectType, objectId);
        } else {
            attachment = attachmentService.createAttachment(objectType, objectId, status.getFileName(), contentType, inputStream, (int) status.getFileSize());
        }
        User user = SecurityHelper.getUser();
        attachment.setUser(user);
        attachmentService.saveAttachment(attachment);

        if (link & !isUpdate) {
            sharedLinkService.getSharedLink(Models.ATTACHMENT.getObjectType(), attachment.getAttachmentId(), true);
        }
    }
    
    private void setObjectTypeAndObjectId(Attachment attachment, int objectType, long objectId) {
       if (attachment instanceof LocalAttachment){
        ((LocalAttachment) attachment).setObjectType(objectType);
        ((LocalAttachment) attachment).setObjectId(objectId);
       }else{
        ((DefaultAttachment) attachment).setObjectType(objectType);
        ((DefaultAttachment) attachment).setObjectId(objectId);
       }
    }

    /**
     * Create a new upload for tus protocol
     * 
     * @param attachmentId
     * @param fileSize
     * @param uploadMetadata
     * @return
     * @throws IOException
     */
    @PostMapping("/{attachmentId:[\\p{Digit}]+}/tus")
    public ResponseEntity<?> createUpload(
            @PathVariable Long attachmentId,
            @RequestHeader(TusHttpHeader.UPLOAD_LENGTH) long fileSize,
            @RequestHeader(TusHttpHeader.UPLOAD_METADATA) String uploadMetadata) throws IOException {
        return handleCreationPostRequest(attachmentId, fileSize, uploadMetadata);
    }

    /**
     * Upload a file chunk for tus protocol
     * 
     * @param attachmentId
     * @param encodedFileName
     * @param offset
     * @param tusVersion
     * @param data
     * @return
     * @throws IOException
     */
    @PatchMapping("/{attachmentId:[\\p{Digit}]+}/tus/{encodedFileName}")
    public ResponseEntity<?> uploadFile(@PathVariable Long attachmentId,
            @PathVariable String encodedFileName,
            @RequestHeader(TusHttpHeader.UPLOAD_OFFSET) long offset,
            @RequestHeader(TusHttpHeader.TUS_RESUMABLE) String tusVersion,
            @RequestBody byte[] data) throws IOException {
        return handleCreationPatchRequest(encodedFileName, offset, tusVersion, data);
    }

    /**
     * Get the upload offset for the file
     * 
     * @param attachmentId
     * @param encodedFileName
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "/{attachmentId:[\\p{Digit}]+}/tus/{encodedFileName}", method = RequestMethod.HEAD)
    public ResponseEntity<?> getFileUploadOffset(
            @PathVariable Long attachmentId,
            @PathVariable String encodedFileName) throws IOException {
        return handleCreationHeadRequest(encodedFileName);
    }

}