package ru.soune.nocopy.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import ru.soune.nocopy.dto.file.CheckStatus;
import ru.soune.nocopy.entity.file.FileEntity;
import ru.soune.nocopy.entity.file.ProtectionStatus;
import ru.soune.nocopy.entity.user.ProtectedFileCheck;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.repository.FileEntityRepository;
import ru.soune.nocopy.repository.UserRepository;
import ru.soune.nocopy.service.file.CheckCounterService;
import ru.soune.nocopy.service.file.FileStorageService;
import ru.soune.nocopy.service.file.ImageResizeService;

import java.io.FileNotFoundException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
@RequestMapping("/api/files")
@Slf4j
public class FileController {

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private FileEntityRepository fileRepository;

    @Autowired
    private CheckCounterService checkCounterService;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ImageResizeService imageResizeService;

    @GetMapping("/public/{fileId}")
    public ResponseEntity<Resource> getPublicFile(@PathVariable String fileId) {
        try {
            FileEntity fileEntity = fileRepository.findById(fileId)
                    .orElseThrow(() -> new FileNotFoundException("Not found: " + fileId));

            Path filePath = Paths.get(fileEntity.getFilePath());
            if (!Files.exists(filePath)) {
                log.error("Not on disk: {}", fileEntity.getFilePath());
                return ResponseEntity.notFound().build();
            }

            Resource resource = fileStorageService.loadFileAsResource(fileEntity.getFilePath());

            MediaType mediaType = getMediaType(fileEntity);

            ContentDisposition contentDisposition = ContentDisposition.inline()
                    .filename(fileEntity.getOriginalFileName(), StandardCharsets.UTF_8)
                    .build();

            return ResponseEntity.ok()
                    .contentType(mediaType)
                    .contentLength(Files.size(filePath))
                    .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString())
                    .header(HttpHeaders.CACHE_CONTROL, "public, max-age=3600")
                    .body(resource);

        } catch (FileNotFoundException e) {
            log.warn("Not on disk: {}", fileId);
            return ResponseEntity.notFound().build();
        } catch (IOException e) {
            log.error("Read file exception: {}", fileId, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    private MediaType getMediaType(FileEntity fileEntity) {
        try {
            return MediaType.parseMediaType(fileEntity.getMimeType());
        } catch (InvalidMediaTypeException e) {
            String extension = fileEntity.getFileExtension().toLowerCase();
            switch (extension) {
                case "jpg":
                case "jpeg":
                    return MediaType.IMAGE_JPEG;
                case "png":
                    return MediaType.IMAGE_PNG;
                case "pdf":
                    return MediaType.APPLICATION_PDF;
                default:
                    return MediaType.APPLICATION_OCTET_STREAM;
            }
        }
    }

    @GetMapping("/protected/{fileId}/{type}")
    public ResponseEntity<Resource> getProtectedFile(
            @PathVariable String fileId, @PathVariable String type) throws IOException {

        FileEntity fileEntity = fileRepository.findById(fileId)
                .orElseThrow(() -> new RuntimeException("File not found"));

        String path = imageResizeService.getPath(fileEntity, type);

        Resource resource = fileStorageService.loadFileAsResource(path);

        if (fileEntity.getProtectionStatus() == ProtectionStatus.NOT_PROTECTED ||
                fileEntity.getProtectionStatus() == ProtectionStatus.PROCESSING) {
            throw new RuntimeException("File is not protected");
        }

        String fileName = fileEntity.getOriginalFileName().replace("." +
                fileEntity.getFileExtension(), "") + "_nocopy_protected" +
                "." + fileEntity.getFileExtension();
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
                .replace("+", "%20");

        return ResponseEntity.ok()
                .contentType(new MediaType(fileEntity.getMimeType(), fileEntity.getFileExtension()))
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "inline; filename*=UTF-8''" + encodedFileName)
                .body(resource);
    }

    @GetMapping("check-file/status/{userId}")
    public ResponseEntity<CheckStatus> getStatus(@PathVariable Long userId) {
        CheckStatus status = checkCounterService.getCurrentStatus(userId);

        return ResponseEntity.ok(status);
    }

    @PostMapping("check-file/update-limit/{userId}/{limit}")
    public ResponseEntity<ProtectedFileCheck> getStatus(@PathVariable Long userId, @PathVariable Integer limit) {
        User user = userRepository.findById(userId).orElseThrow();
        ProtectedFileCheck protectedFileCheck = checkCounterService.updateLimit(user, limit);

        return ResponseEntity
                .ok()
                .body(protectedFileCheck);
    }
}
