package de.votian.services.controller;

import de.votian.services.dto.JobAttachmentDto;
import de.votian.services.dto.JobPhotoDto;
import de.votian.services.dto.UserSessionDto;
import de.votian.services.entity.Job;
import de.votian.services.security.AccessControlService;
import de.votian.services.service.JobMediaService;
import de.votian.services.service.JobService;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@RestController
@RequestMapping("/api/jobs")
public class JobMediaController {

    private final JobMediaService jobMediaService;
    private final JobService jobService;
    private final AccessControlService accessControlService;

    public JobMediaController(JobMediaService jobMediaService,
                              JobService jobService,
                              AccessControlService accessControlService) {
        this.jobMediaService = jobMediaService;
        this.jobService = jobService;
        this.accessControlService = accessControlService;
    }

    @GetMapping("/{id}/attachments")
    public ResponseEntity<List<JobAttachmentDto>> getAttachments(@PathVariable Long id, Authentication authentication) {
        if (!canAccessJob(authentication, id)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        return ResponseEntity.ok(jobMediaService.getAttachments(id));
    }

    @PostMapping("/{id}/attachments")
    public ResponseEntity<JobAttachmentDto> uploadAttachment(@PathVariable Long id,
                                                             @RequestParam("file") MultipartFile file,
                                                             @RequestParam(name = "remark", required = false) String remark,
                                                             Authentication authentication) {
        if (!canManageJob(authentication, id)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        return ResponseEntity.ok(jobMediaService.saveAttachment(id, file, remark));
    }

    @DeleteMapping("/{id}/attachments/{attachmentId}")
    public ResponseEntity<Void> deleteAttachment(@PathVariable Long id,
                                                 @PathVariable Long attachmentId,
                                                 Authentication authentication) {
        if (!canManageJob(authentication, id)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        jobMediaService.deleteAttachment(id, attachmentId);
        return ResponseEntity.ok().build();
    }

    @GetMapping("/{id}/attachments/content")
    public ResponseEntity<ByteArrayResource> downloadAttachment(@PathVariable Long id,
                                                                @RequestParam("name") String fileName,
                                                                Authentication authentication) {
        if (!canAccessJob(authentication, id)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        byte[] data = jobMediaService.loadAttachment(id, fileName);
        return ResponseEntity.ok()
                .contentType(Objects.requireNonNull(MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM)))
                .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.inline().filename(fileName).build().toString())
                .body(new ByteArrayResource(Objects.requireNonNull(data)));
    }

    @GetMapping("/{id}/photos")
    public ResponseEntity<List<JobPhotoDto>> getPhotos(@PathVariable Long id, Authentication authentication) {
        if (!canAccessJob(authentication, id)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        return ResponseEntity.ok(jobMediaService.getPhotos(id));
    }

    @GetMapping("/{id}/photos/content")
    public ResponseEntity<ByteArrayResource> downloadPhoto(@PathVariable Long id,
                                                           @RequestParam("name") String fileName,
                                                           Authentication authentication) {
        if (!canAccessJob(authentication, id)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        byte[] data = jobMediaService.loadPhoto(id, fileName);
        return ResponseEntity.ok()
                .contentType(Objects.requireNonNull(MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM)))
                .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.inline().filename(fileName).build().toString())
                .body(new ByteArrayResource(Objects.requireNonNull(data)));
    }

    private boolean canAccessJob(Authentication authentication, Long jobId) {
        UserSessionDto user = accessControlService.sessionUser(authentication);
        Job job = jobService.findById(jobId).orElse(null);
        return job != null && accessControlService.canAccessJob(user, job);
    }

    private boolean canManageJob(Authentication authentication, Long jobId) {
        UserSessionDto user = accessControlService.sessionUser(authentication);
        Job job = jobService.findById(jobId).orElse(null);
        return job != null && accessControlService.canAccessJob(user, job) && accessControlService.canManageJobs(user);
    }
}
