package dev.awesomedev.mirim.domain;

import jakarta.persistence.*;

import java.time.Instant;

/**
 * 이 파일이 하는 일:
 * 수습생이 과제에 대해 제출한 결과물 하나를 표현하는 JPA 엔티티다.
 * 같은 과제를 다시 제출하면 새 행을 만들지 않고 기존 행을 갱신한다(서비스 계층에서 처리).
 * 멘토가 피드백을 남기면 status가 SUBMITTED → REVIEWED로 바뀐다.
 */
@Entity
// 학습 포인트: "학생당 과제당 제출물 1개" 규칙의 최종 방어선은 DB 유니크 제약이다.
// 서비스의 '있으면 갱신, 없으면 생성' 검사(check-then-insert)는 거의 동시에 온
// 두 요청(두 탭에서 동시 제출 등)이 둘 다 "없네? 생성!"을 통과할 수 있다.
// 그러면 중복 행이 생기고, 이후 findByAssignmentAndUser(Optional 반환)가 2건을 만나
// 그 과제의 재제출이 전부 500으로 죽는다. 제약이 있으면 최악의 경우에도 DB가 한쪽을 거절한다.
// (CodingSubmission·Progress·CourseProgress도 같은 이유로 전부 이 제약을 갖고 있다.)
@Table(name = "submissions",
        uniqueConstraints = @UniqueConstraint(columnNames = {"assignment_id", "user_id"}))
public class Submission {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 어떤 과제에 대한 제출인지.
     * DB에서는 submissions 테이블에 assignment_id 외래키(FK) 컬럼이 생긴다.
     *
     * 학습 포인트: @ManyToOne의 fetch 기본값은 EAGER(즉시 로딩) — Submission을 읽을 때
     * Assignment도 함께 JOIN해서 가져온다. 제출물은 항상 과제 제목·학생 이름과 함께
     * 화면에 보여주므로 EAGER를 그대로 둔다. 목록이 커지면 fetch join으로 바꾸는 게 다음 단계다.
     * (Progress 엔티티는 반대로 LAZY를 쓴다 — 두 파일을 비교해 보자.)
     */
    @ManyToOne(optional = false)
    @JoinColumn(name = "assignment_id")
    private Assignment assignment;

    /** 제출한 수습생. DB에는 user_id 외래키 컬럼이 된다. */
    @ManyToOne(optional = false)
    @JoinColumn(name = "user_id")
    private User user;

    /** 제출 내용 (긴 글이 될 수 있어 text 타입) */
    @Column(columnDefinition = "text", nullable = false)
    private String content;

    /** 결과물 링크 (깃 저장소, 배포 URL 등). 없으면 null. */
    private String link;

    /** 제출(또는 재제출) 시각 */
    @Column(nullable = false)
    private Instant submittedAt;

    /** 멘토의 피드백. 아직 리뷰 전이면 null. */
    @Column(columnDefinition = "text")
    private String feedback;

    /** 상태: SUBMITTED(제출됨) | REVIEWED(피드백 완료) */
    @Column(nullable = false)
    private String status;

    protected Submission() {
    }

    public Submission(Assignment assignment, User user, String content, String link) {
        this.assignment = assignment;
        this.user = user;
        this.content = content;
        this.link = link;
        this.submittedAt = Instant.now();
        this.status = "SUBMITTED";
    }

    /** 재제출: 내용과 링크를 바꾸고 제출 시각을 갱신, 상태를 다시 SUBMITTED로 되돌린다. */
    public void resubmit(String content, String link) {
        this.content = content;
        this.link = link;
        this.submittedAt = Instant.now();
        this.status = "SUBMITTED";
    }

    /** 멘토 피드백 등록: 피드백을 저장하고 상태를 REVIEWED로 바꾼다. */
    public void review(String feedback) {
        this.feedback = feedback;
        this.status = "REVIEWED";
    }

    public Long getId() {
        return id;
    }

    public Assignment getAssignment() {
        return assignment;
    }

    public User getUser() {
        return user;
    }

    public String getContent() {
        return content;
    }

    public String getLink() {
        return link;
    }

    public Instant getSubmittedAt() {
        return submittedAt;
    }

    public String getFeedback() {
        return feedback;
    }

    public String getStatus() {
        return status;
    }
}
