package com.devjob.controller;

import com.devjob.domain.JobStatus;
import com.devjob.dto.ApiResponse;
import com.devjob.dto.JobCreateRequest;
import com.devjob.dto.JobResponse;
import com.devjob.dto.JobSearchRequest;
import com.devjob.dto.JobUpdateRequest;
import com.devjob.dto.PageResponse;
import com.devjob.security.CustomUserDetails;
import com.devjob.service.JobService;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.net.URI;
import java.time.ZoneOffset;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 채용 공고 API.
 *
 * - POST   /api/jobs       → 201 Created + Location 헤더
 * - GET    /api/jobs/{id}  → 200 OK / 404 Not Found
 * - GET    /api/jobs       → status + Pageable(page/size/sort) 기반 페이징 목록
 * - PUT    /api/jobs/{id}  → 200 OK (전체 교체)
 * - GET    /api/jobs/search → 제목/회사명/지역 통합 검색
 * - DELETE /api/jobs/{id}  → 204 No Content
 */
@Tag(name = "채용 공고", description = "Job API")
@RestController
@RequestMapping("/api/jobs")
@RequiredArgsConstructor
public class JobController {

    private final JobService jobService;

    @Operation(summary = "채용 공고 등록", description = "새 채용 공고를 등록합니다")
    @PostMapping
    public ResponseEntity<ApiResponse<JobResponse>> create(@RequestBody @Valid JobCreateRequest req,
            @AuthenticationPrincipal CustomUserDetails user) {
        JobResponse job = jobService.create(req, user.getId());
        URI location = URI.create("/api/jobs/" + job.id());
        return ResponseEntity.created(location)
                .body(ApiResponse.ok(job));
    }

    /**
     * EP45: ETag/If-None-Match를 이용한 조건부 요청 처리.
     * - updatedAt(초 단위 epoch)을 ETag로 사용한다. 데이터가 바뀌지 않았다면 304를 반환해
     *   응답 본문 전송을 생략한다.
     * - Cache-Control: max-age=60 → 60초 동안은 브라우저가 재요청 없이 캐시를 사용하고,
     *   이후에는 재검증(조건부 요청)한다. 공고처럼 자주 바뀌는 데이터에 적합한 값이다.
     */
    @Operation(summary = "채용 공고 단건 조회")
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<JobResponse>> getOne(
            @PathVariable Long id,
            @RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) {
        return jobService.findById(id)
                .map(job -> {
                    String etag = "\"" + job.updatedAt().toEpochSecond(ZoneOffset.UTC) + "\"";
                    if (etag.equals(ifNoneMatch)) {
                        return ResponseEntity.status(304).<ApiResponse<JobResponse>>build();
                    }
                    return ResponseEntity.ok()
                            .header("ETag", etag)
                            .header("Cache-Control", "max-age=60, must-revalidate")
                            .body(ApiResponse.ok(job));
                })
                .orElse(ResponseEntity.notFound().build());
    }

    @Operation(summary = "채용 공고 목록 조회", description = "상태(status)별 페이징 목록을 조회합니다")
    @GetMapping
    public ApiResponse<PageResponse<JobResponse>> getJobs(
            @RequestParam(defaultValue = "OPEN") JobStatus status,
            @PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
        return ApiResponse.ok(jobService.findByStatus(status, pageable));
    }

    @Operation(summary = "채용 공고 수정", description = "title/description/salary를 전체 교체합니다")
    @PutMapping("/{id}")
    public ApiResponse<JobResponse> update(@PathVariable Long id, @RequestBody @Valid JobUpdateRequest req,
            @AuthenticationPrincipal CustomUserDetails user) {
        return ApiResponse.ok(jobService.update(id, req, user.getId(), isAdmin(user)));
    }

    /**
     * EP27: keyword/location/status/minSalary/maxSalary를 JobSearchRequest(@ModelAttribute)로 받아
     * Specification 동적 쿼리로 검색한다. 모든 조건은 선택값이다.
     */
    @Operation(summary = "채용 공고 검색", description = "키워드/지역/상태/연봉 범위로 공고를 동적 검색합니다")
    @GetMapping("/search")
    public ApiResponse<PageResponse<JobResponse>> search(
            @ModelAttribute JobSearchRequest request,
            @PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
        return ApiResponse.ok(jobService.search(request, pageable));
    }

    @Operation(summary = "채용 공고 삭제")
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id,
            @AuthenticationPrincipal CustomUserDetails user) {
        jobService.delete(id, user.getId(), isAdmin(user));
        return ResponseEntity.noContent().build();
    }

    /** ROLE_ADMIN 보유 여부. */
    private boolean isAdmin(CustomUserDetails user) {
        return user.getAuthorities().stream()
                .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
    }
}
