package com.agent.management.model.entity;

import com.agent.management.model.common.CredentialHolder;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * 知识图谱数据源（Neo4j）
 *
 * <p>password 字段使用 Jasypt 加密入库，{@code @Transient} 的 passwordPlain
 * 仅在内存中临时持有；Controller 返回时永远不包含 password 字段。</p>
 *
 * <p><b>字段语义</b>（详见 {@link CredentialHolder}）：</p>
 * <ul>
 *   <li>{@code passwordEnc}：永远承载 Jasypt 密文</li>
 *   <li>{@code passwordPlain}：仅表单提交时携带</li>
 *   <li>{@code decryptedPassword}：仅运行时使用，{@code @Transient} + {@code @JsonIgnore}，
 *       永不入库、永不序列化到 API 响应</li>
 * </ul>
 */
@Data
@Entity
@Table(name = "kb_graph_source")
public class GraphSource implements CredentialHolder {

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

    @NotBlank(message = "图谱源名称不能为空")
    @Size(max = 100, message = "图谱源名称长度不能超过 100")
    @Column(nullable = false, length = 100)
    private String name;

    /**
     * Neo4j Bolt URI（如 bolt://localhost:7687 或 neo4j+s://xxx.databases.neo4j.io）
     */
    @NotBlank(message = "Neo4j URI 不能为空")
    @Size(max = 500, message = "URI 长度不能超过 500")
    @Column(name = "uri", nullable = false, length = 500)
    private String uri;

    @NotBlank(message = "用户名不能为空")
    @Size(max = 100, message = "用户名长度不能超过 100")
    @Column(nullable = false, length = 100)
    private String username;

    /**
     * Jasypt 加密后的密码（永远不会通过 API 返回）
     */
    @Column(name = "password_enc", nullable = false, length = 1024)
    private String passwordEnc;

    /**
     * 临时持有的明文密码（不入库，仅用于编辑时回填空值/重新加密）。
     *
     * <p>Jackson 配置：{@link JsonProperty.Access#WRITE_ONLY} 表示仅接收前端 POST 的明文，
     * 序列化到响应时跳过此字段（API 返回永不包含明文密码）。</p>
     */
    @Transient
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String passwordPlain;

    /**
     * 运行时解密后的明文密码（不入库、不出 API）。
     * 由 {@link com.agent.management.service.CredentialManager#decryptToTransient} 写入，
     * 由 {@link com.agent.management.service.Neo4jExecutorService} 读取。
     */
    @Transient
    @JsonIgnore
    private String decryptedPassword;

    /**
     * 默认 database（Neo4j Community 仅 default；Enterprise 可自定义）
     */
    @Size(max = 100, message = "database 长度不能超过 100")
    @Column(length = 100)
    private String database;

    /**
     * 测试连接结果：SUCCESS / FAILED / UNTESTED
     */
    @Column(name = "test_status", length = 20)
    private String testStatus = "UNTESTED";

    @Column(name = "test_message", length = 500)
    private String testMessage;

    @Column(name = "created_at")
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        LocalDateTime now = LocalDateTime.now();
        if (createdAt == null) createdAt = now;
        updatedAt = now;
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }
}
