package com.codebyte.api.auth.domain;

import com.codebyte.api.common.audit.BaseAuditableEntity;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import java.time.Instant;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.UuidGenerator;

/**
 * User account. The {@code password} field holds an Argon2id hash — never a plaintext password.
 * Login lockout fields ({@code failedLoginAttempts}, {@code lockedUntil}) are populated in Phase 5.
 */
@Entity
@Table(name = "users")
@Getter
@Setter
public class User extends BaseAuditableEntity {

    @Id
    @UuidGenerator(style = UuidGenerator.Style.TIME)
    @Column(name = "id", nullable = false, updatable = false)
    private UUID id;

    @Column(name = "email", nullable = false, length = 320)
    private String email;

    /** Argon2id hash of the password, never the plaintext. */
    @Column(name = "password", nullable = false, length = 255)
    private String password;

    @Column(name = "activated_account", nullable = false)
    private boolean activatedAccount;

    @Column(name = "activated_at")
    private Instant activatedAt;

    @Column(name = "failed_login_attempts", nullable = false)
    private int failedLoginAttempts;

    @Column(name = "locked_until")
    private Instant lockedUntil;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
    @Column(name = "role", nullable = false, length = 64)
    private Set<String> roles = new HashSet<>();

    @Version
    @Column(name = "version", nullable = false)
    private long version;

    protected User() {}

    public User(String email, String password) {
        this.email = email;
        this.password = password;
    }

    public void addRole(String role) {
        roles.add(role);
    }

    /** Marks the account active. Single-use activation is enforced by the token, not here. */
    public void activate(Instant at) {
        this.activatedAccount = true;
        this.activatedAt = at;
    }

    public boolean isLocked(Instant now) {
        return lockedUntil != null && lockedUntil.isAfter(now);
    }

    /**
     * Records a failed login. After {@code maxAttempts} consecutive failures the account is locked
     * for {@code lockDuration}.
     */
    public void recordFailedLogin(int maxAttempts, java.time.Duration lockDuration, Instant now) {
        this.failedLoginAttempts++;
        if (this.failedLoginAttempts >= maxAttempts) {
            this.lockedUntil = now.plus(lockDuration);
        }
    }

    /** Clears the failure counter and any lock after a successful login. */
    public void recordSuccessfulLogin() {
        this.failedLoginAttempts = 0;
        this.lockedUntil = null;
    }
}
