package kr.itn.itnhub.project;

import jakarta.validation.Valid;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.List;
import java.util.Map;

/** 사업관리 화면이 쓰는 엔드포인트. 기관 현황 목록 + 연락 이력 + 최종 보고서. */
@RestController
public class ProjectController {

    private final ProjectService projectService;
    private final kr.itn.itnhub.config.CurrentUser currentUser;

    public ProjectController(ProjectService projectService,
                             kr.itn.itnhub.config.CurrentUser currentUser) {
        this.projectService = projectService;
        this.currentUser = currentUser;
    }

    @GetMapping("/api/projects")
    public List<ProjectRow> rows() {
        return projectService.rows();
    }

    @GetMapping("/api/orgs/{id}/contact-logs")
    public List<ContactLog> logs(@PathVariable("id") Long orgId) {
        return projectService.logs(orgId);
    }

    /** 작성자는 업무메모와 마찬가지로 로그인 사용자를 서버가 기록한다. */
    @PostMapping("/api/orgs/{id}/contact-logs")
    public List<ContactLog> addLog(@PathVariable("id") Long orgId,
                                   @Valid @RequestBody ContactLogRequest request,
                                   Principal principal) {
        return projectService.addLog(orgId, request, currentUser.displayName(principal));
    }

    @PutMapping("/api/orgs/{id}/contact-logs/{logId}")
    public List<ContactLog> updateLog(@PathVariable("id") Long orgId, @PathVariable Long logId,
                                      @Valid @RequestBody ContactLogRequest request) {
        return projectService.updateLog(orgId, logId, request);
    }

    @ResponseStatus(HttpStatus.NO_CONTENT)
    @DeleteMapping("/api/orgs/{id}/contact-logs/{logId}")
    public void deleteLog(@PathVariable("id") Long orgId, @PathVariable Long logId) {
        projectService.deleteLog(orgId, logId);
    }

    @GetMapping("/api/orgs/{id}/reports")
    public List<OrgReport> reports(@PathVariable("id") Long orgId) {
        return projectService.reports(orgId);
    }

    @PostMapping("/api/orgs/{id}/reports")
    public List<OrgReport> addReport(@PathVariable("id") Long orgId,
                                     @RequestPart("file") MultipartFile file,
                                     Principal principal) throws IOException {
        String name = file.getOriginalFilename() == null ? "report" : file.getOriginalFilename();
        return projectService.addReport(orgId, name, file.getContentType(), file.getBytes(),
                currentUser.displayName(principal));
    }

    /** 한글 파일명이 깨지지 않게 ChannelFeedController.download와 같은 RFC 5987 인코딩을 쓴다. */
    @GetMapping("/api/orgs/{id}/reports/{reportId}")
    public ResponseEntity<byte[]> downloadReport(@PathVariable("id") Long orgId,
                                                 @PathVariable Long reportId) {
        ProjectMapper.ReportContent report = projectService.reportContent(orgId, reportId);
        String encodedName = URLEncoder.encode(report.fileName(), StandardCharsets.UTF_8)
                .replace("+", "%20");
        MediaType type = report.contentType() == null || report.contentType().isBlank()
                ? MediaType.APPLICATION_OCTET_STREAM
                : MediaType.parseMediaType(report.contentType());

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedName)
                .contentType(type)
                .body(report.content());
    }

    /** 올려둔 보고서를 새 파일로 바꾼다(요구사항 [25]). 목록에 새 줄이 생기지 않는다. */
    @PutMapping("/api/orgs/{id}/reports/{reportId}")
    public List<OrgReport> replaceReport(@PathVariable("id") Long orgId,
                                         @PathVariable Long reportId,
                                         @RequestPart("file") MultipartFile file,
                                         Principal principal) throws IOException {
        String name = file.getOriginalFilename() == null ? "report" : file.getOriginalFilename();
        return projectService.replaceReport(orgId, reportId, name, file.getContentType(),
                file.getBytes(), currentUser.displayName(principal));
    }

    @ResponseStatus(HttpStatus.NO_CONTENT)
    @DeleteMapping("/api/orgs/{id}/reports/{reportId}")
    public void deleteReport(@PathVariable("id") Long orgId, @PathVariable Long reportId) {
        projectService.deleteReport(orgId, reportId);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(IllegalArgumentException.class)
    public Map<String, String> handleBadRequest(IllegalArgumentException e) {
        return Map.of("message", e.getMessage());
    }
}
