create table public.chat_messages
(
    id               serial
        primary key,
    thread_id        varchar(255) not null,
    checkpoint_id    varchar(255) not null,
    message_index    integer      not null,
    role             varchar(20)  not null,
    content          text         not null,
    injected_content text,
    has_files        boolean                  default false,
    metadata         jsonb,
    created_at       timestamp with time zone default CURRENT_TIMESTAMP,
    name             varchar(255),
    constraint uk_checkpoint_message
        unique (checkpoint_id, message_index)
);

comment on table public.chat_messages is '聊天消息表，存储用户原始消息和AI响应的关键信息';

comment on column public.chat_messages.thread_id is '会话线程ID';

comment on column public.chat_messages.checkpoint_id is '关联的checkpoint ID';

comment on column public.chat_messages.message_index is '消息在checkpoint中的索引（从0开始）';

comment on column public.chat_messages.role is '消息角色：user、assistant、system、tool';

comment on column public.chat_messages.content is '用户的原始问题或AI的响应';

comment on column public.chat_messages.injected_content is '注入给AI的完整内容（包含文件内容）';

comment on column public.chat_messages.has_files is '是否关联了文件';

comment on column public.chat_messages.metadata is '额外信息：token、模型、推理内容等';

comment on column public.chat_messages.name is '工具消息时的工具名称（如 internet_search、text_to_image）';

alter table public.chat_messages
    owner to zuoleiroot;

create index idx_chat_messages_thread_id
    on public.chat_messages (thread_id);

create index idx_chat_messages_checkpoint_id
    on public.chat_messages (checkpoint_id);

create index idx_chat_messages_thread_created
    on public.chat_messages (thread_id asc, created_at desc);

create index idx_chat_messages_role
    on public.chat_messages (role);

create index idx_chat_messages_has_files
    on public.chat_messages (has_files);

create index idx_chat_messages_metadata
    on public.chat_messages using gin (metadata);

create index idx_chat_messages_content_search
    on public.chat_messages using gin (to_tsvector('simple'::regconfig, content));

create table public.checkpoint_migrations
(
    v integer not null
        primary key
);

alter table public.checkpoint_migrations
    owner to zuoleiroot;

create table public.checkpoints
(
    thread_id            text                      not null,
    checkpoint_ns        text  default ''::text    not null,
    checkpoint_id        text                      not null,
    parent_checkpoint_id text,
    type                 text,
    checkpoint           jsonb                     not null,
    metadata             jsonb default '{}'::jsonb not null,
    primary key (thread_id, checkpoint_ns, checkpoint_id)
);

alter table public.checkpoints
    owner to zuoleiroot;

create index checkpoints_thread_id_idx
    on public.checkpoints (thread_id);

create table public.checkpoint_blobs
(
    thread_id     text                  not null,
    checkpoint_ns text default ''::text not null,
    channel       text                  not null,
    version       text                  not null,
    type          text                  not null,
    blob          bytea,
    primary key (thread_id, checkpoint_ns, channel, version)
);

alter table public.checkpoint_blobs
    owner to zuoleiroot;

create index checkpoint_blobs_thread_id_idx
    on public.checkpoint_blobs (thread_id);

create table public.checkpoint_writes
(
    thread_id     text                  not null,
    checkpoint_ns text default ''::text not null,
    checkpoint_id text                  not null,
    task_id       text                  not null,
    idx           integer               not null,
    channel       text                  not null,
    type          text,
    blob          bytea                 not null,
    task_path     text default ''::text not null,
    primary key (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
);

alter table public.checkpoint_writes
    owner to zuoleiroot;

create index checkpoint_writes_thread_id_idx
    on public.checkpoint_writes (thread_id);

create table public.enterprise
(
    id         serial
        primary key,
    name       varchar(255)                                       not null,
    code       varchar(64)
        unique,
    created_at timestamp with time zone default CURRENT_TIMESTAMP not null,
    updated_at timestamp with time zone default CURRENT_TIMESTAMP not null
);

comment on table public.enterprise is '企业（单租户部署通常仅一条记录）';

alter table public.enterprise
    owner to zuoleiroot;

create table public.department
(
    id            serial
        primary key,
    enterprise_id integer                                            not null
        references public.enterprise
            on delete cascade,
    name          varchar(255)                                       not null,
    parent_id     integer
                                                                     references public.department
                                                                         on delete set null,
    created_at    timestamp with time zone default CURRENT_TIMESTAMP not null,
    updated_at    timestamp with time zone default CURRENT_TIMESTAMP not null,
    constraint uq_department_enterprise_name
        unique (enterprise_id, name)
);

comment on table public.department is '部门';

alter table public.department
    owner to zuoleiroot;

create table public.user_list
(
    id                      serial
        primary key,
    username                varchar(50)                                                    not null
        unique,
    email                   varchar(255)                                                   not null
        constraint unique_email
            unique,
    phone                   varchar(255)                                                   not null
        unique,
    github_id               varchar(100)
        constraint unique_github_id
            unique,
    github_username         varchar(100),
    github_avatar_url       text,
    github_access_token     text,
    github_token_expires_at timestamp with time zone,
    display_name            varchar(100),
    avatar_url              text,
    bio                     text,
    is_active               boolean                  default true,
    email_verified          boolean                  default false,
    created_at              timestamp with time zone default CURRENT_TIMESTAMP,
    updated_at              timestamp with time zone default CURRENT_TIMESTAMP,
    last_login_at           timestamp with time zone,
    hashed_password         varchar(255),
    is_search               boolean                  default false,
    is_reasoner             boolean                  default false,
    enterprise_id           integer                  default 1                             not null
        references public.enterprise,
    department_id           integer
                                                                                           references public.department
                                                                                               on delete set null,
    role                    varchar(32)              default 'employee'::character varying not null,
    is_first_login          boolean                  default true                          not null
);

comment on column public.user_list.role is 'admin | leader | employee';

comment on column public.user_list.is_first_login is '首次登录可强制改密（可选业务）';

alter table public.user_list
    owner to zuoleiroot;

create index idx_user_list_github_id
    on public.user_list (github_id);

create index idx_user_list_email
    on public.user_list (email);

create index idx_user_list_username
    on public.user_list (username);

create index idx_user_list_created_at
    on public.user_list (created_at);

create index idx_user_list_enterprise_id
    on public.user_list (enterprise_id);

create index idx_user_list_department_id
    on public.user_list (department_id);

create index idx_user_list_role
    on public.user_list (role);

create table public.knowledge_base
(
    id            serial
        primary key,
    user_id       integer                                                       not null,
    name          varchar(255)                                                  not null,
    description   text,
    created_at    timestamp with time zone default CURRENT_TIMESTAMP,
    updated_at    timestamp with time zone default CURRENT_TIMESTAMP,
    is_deleted    boolean                  default false,
    deleted_at    timestamp with time zone,
    enterprise_id integer                  default 1                            not null
        references public.enterprise,
    department_id integer
                                                                                references public.department
                                                                                    on delete set null,
    creator_id    integer                                                       not null
        references public.user_list
            on delete set null,
    visibility    varchar(32)              default 'private'::character varying not null
        constraint ck_knowledge_base_visibility
            check ((visibility)::text = ANY
                   ((ARRAY ['private'::character varying, 'department'::character varying, 'enterprise'::character varying])::text[]))
);

comment on column public.knowledge_base.creator_id is '创建者（与 user_id 通常一致，用于权限判断）';

comment on column public.knowledge_base.visibility is 'private | department | enterprise';

alter table public.knowledge_base
    owner to zuoleiroot;

create index idx_knowledge_base_user_id
    on public.knowledge_base (user_id);

create index idx_knowledge_base_user_name
    on public.knowledge_base (user_id, name);

create index idx_knowledge_base_created_at
    on public.knowledge_base (created_at);

create index idx_knowledge_base_is_deleted
    on public.knowledge_base (is_deleted);

create index idx_knowledge_base_user_deleted
    on public.knowledge_base (user_id, is_deleted);

create unique index uk_user_knowledge_base_name_active
    on public.knowledge_base (user_id, name)
    where (is_deleted = false);

create index idx_knowledge_base_enterprise
    on public.knowledge_base (enterprise_id);

create index idx_knowledge_base_creator
    on public.knowledge_base (creator_id);

create index idx_knowledge_base_ent_vis
    on public.knowledge_base (enterprise_id, visibility)
    where (is_deleted = false);

create table public.knowledge_base_file
(
    id                serial
        primary key,
    knowledge_base_id integer                                                          not null
        constraint fk_knowledge_base
            references public.knowledge_base
            on delete cascade,
    user_id           integer                                                          not null,
    file_name         varchar(255)                                                     not null,
    file_path         varchar(500)                                                     not null,
    file_size         bigint                                                           not null,
    file_type         varchar(50)              default 'pdf'::character varying        not null,
    status            varchar(20)              default 'processing'::character varying not null,
    chunk_count       integer                  default 0,
    created_at        timestamp with time zone default CURRENT_TIMESTAMP,
    updated_at        timestamp with time zone default CURRENT_TIMESTAMP,
    is_deleted        boolean                  default false,
    deleted_at        timestamp with time zone
);

alter table public.knowledge_base_file
    owner to zuoleiroot;

create index idx_kb_file_kb_id
    on public.knowledge_base_file (knowledge_base_id);

create index idx_kb_file_user_id
    on public.knowledge_base_file (user_id);

create index idx_kb_file_status
    on public.knowledge_base_file (status);

create index idx_kb_file_created_at
    on public.knowledge_base_file (created_at);

create unique index idx_kb_file_unique_active
    on public.knowledge_base_file (knowledge_base_id, file_name)
    where (is_deleted = false);

create table public.knowledge_base_chunk
(
    id                serial
        primary key,
    file_id           integer not null
        constraint fk_kb_file
            references public.knowledge_base_file
            on delete cascade,
    knowledge_base_id integer not null
        constraint fk_kb
            references public.knowledge_base
            on delete cascade,
    chunk_index       integer not null,
    content           text    not null,
    metadata          jsonb,
    vector_id         varchar(255),
    created_at        timestamp with time zone default CURRENT_TIMESTAMP,
    summary           text
);

alter table public.knowledge_base_chunk
    owner to zuoleiroot;

create index idx_kb_chunk_file_id
    on public.knowledge_base_chunk (file_id);

create index idx_kb_chunk_kb_id
    on public.knowledge_base_chunk (knowledge_base_id);

create index idx_kb_chunk_vector_id
    on public.knowledge_base_chunk (vector_id);

create index idx_kb_chunk_metadata
    on public.knowledge_base_chunk using gin (metadata);

create table public.chat_thread_file
(
    id          serial
        primary key,
    thread_id   varchar(255) not null,
    user_id     integer      not null
        constraint fk_chat_thread_file_user
            references public.user_list
            on delete cascade,
    file_name   varchar(255) not null,
    file_path   varchar(500) not null,
    file_size   integer                  default 0,
    file_type   varchar(50)              default 'pdf'::character varying,
    status      varchar(20)              default 'processing'::character varying,
    chunk_count integer                  default 0,
    created_at  timestamp with time zone default CURRENT_TIMESTAMP,
    updated_at  timestamp with time zone default CURRENT_TIMESTAMP,
    is_deleted  boolean                  default false,
    deleted_at  timestamp with time zone
);

alter table public.chat_thread_file
    owner to zuoleiroot;

create index idx_chat_thread_file_thread_id
    on public.chat_thread_file (thread_id);

create index idx_chat_thread_file_user_id
    on public.chat_thread_file (user_id);

create index idx_chat_thread_file_thread_user
    on public.chat_thread_file (thread_id, user_id);

create index idx_chat_thread_file_status
    on public.chat_thread_file (status);

create index idx_chat_thread_file_is_deleted
    on public.chat_thread_file (is_deleted);

create index idx_chat_thread_file_created_at
    on public.chat_thread_file (created_at);

create index idx_chat_thread_file_thread_deleted
    on public.chat_thread_file (thread_id, is_deleted);

create unique index uk_chat_thread_file_thread_name_active
    on public.chat_thread_file (thread_id, file_name)
    where (is_deleted = false);

create table public.chat_thread_chunk
(
    id          serial
        primary key,
    file_id     integer      not null
        constraint fk_chat_thread_chunk_file
            references public.chat_thread_file
            on delete cascade,
    thread_id   varchar(255) not null,
    chunk_index integer      not null,
    content     text         not null,
    metadata    jsonb,
    vector_id   varchar(255),
    created_at  timestamp with time zone default CURRENT_TIMESTAMP,
    summary     text
);

alter table public.chat_thread_chunk
    owner to zuoleiroot;

create index idx_chat_thread_chunk_file_id
    on public.chat_thread_chunk (file_id);

create index idx_chat_thread_chunk_thread_id
    on public.chat_thread_chunk (thread_id);

create index idx_chat_thread_chunk_file_thread
    on public.chat_thread_chunk (file_id, thread_id);

create index idx_chat_thread_chunk_vector_id
    on public.chat_thread_chunk (vector_id);

create index idx_chat_thread_chunk_created_at
    on public.chat_thread_chunk (created_at);

create table public.chat_message_file
(
    id            serial
        primary key,
    thread_id     varchar(255) not null,
    checkpoint_id varchar(255) not null,
    message_index integer      not null,
    file_id       integer      not null
        constraint fk_chat_message_file_file
            references public.chat_thread_file
            on delete cascade,
    created_at    timestamp with time zone default CURRENT_TIMESTAMP,
    constraint uk_message_file
        unique (checkpoint_id, message_index, file_id)
);

alter table public.chat_message_file
    owner to zuoleiroot;

create index idx_chat_message_file_thread_id
    on public.chat_message_file (thread_id);

create index idx_chat_message_file_checkpoint
    on public.chat_message_file (checkpoint_id, message_index);

create index idx_chat_message_file_file_id
    on public.chat_message_file (file_id);

create index idx_chat_message_file_thread_checkpoint
    on public.chat_message_file (thread_id, checkpoint_id);

create table public.graphs
(
    id              serial
        primary key,
    user_id         integer                                                         not null
        constraint fk_graphs_user
            references public.user_list
            on delete cascade,
    name            varchar(255)                                                    not null,
    description     text,
    csv_file_name   varchar(255),
    node_count      integer                  default 0,
    edge_count      integer                  default 0,
    neo4j_graph_id  varchar(100)                                                    not null
        unique,
    graph_type      varchar(20)              default 'knowledge'::character varying not null,
    build_status    varchar(20),
    build_error     text,
    rag_chunk_count integer                  default 0                              not null,
    created_at      timestamp with time zone default CURRENT_TIMESTAMP,
    updated_at      timestamp with time zone default CURRENT_TIMESTAMP,
    enterprise_id   integer                                                         not null
        references public.enterprise,
    department_id   integer
                                                                                    references public.department
                                                                                        on delete set null,
    creator_id      integer                                                         not null
        references public.user_list
            on delete set null,
    visibility      varchar(32)              default 'private'::character varying   not null
        constraint ck_graphs_visibility
            check ((visibility)::text = ANY
                   ((ARRAY ['private'::character varying, 'department'::character varying, 'enterprise'::character varying])::text[]))
);

comment on table public.graphs is '知识图谱元数据表，图数据在 Neo4j；向量块数量见 rag_chunk_count';

comment on column public.graphs.neo4j_graph_id is 'Neo4j 中图谱唯一标识';

comment on column public.graphs.graph_type is '兼容字段，默认 knowledge';

comment on column public.graphs.build_status is '构建状态：pending/processing/completed/failed';

comment on column public.graphs.build_error is '构建失败时的错误信息';

comment on column public.graphs.rag_chunk_count is 'Chroma 中知识图谱 RAG 分块数量';

comment on column public.graphs.creator_id is '创建者（与 user_id 通常一致，用于权限判断）';

comment on column public.graphs.visibility is 'private | department | enterprise';

alter table public.graphs
    owner to zuoleiroot;

create index idx_graphs_user_id
    on public.graphs (user_id);

create index idx_graphs_created_at
    on public.graphs (created_at desc);

create index idx_graphs_neo4j_id
    on public.graphs (neo4j_graph_id);

create index idx_graphs_graph_type
    on public.graphs (user_id, graph_type);

create index idx_graphs_enterprise
    on public.graphs (enterprise_id);

create index idx_graphs_creator
    on public.graphs (creator_id);

create index idx_graphs_ent_vis
    on public.graphs (enterprise_id, visibility);

create table public.chat_threads
(
    id                 serial
        primary key,
    thread_id          varchar(255) not null
        constraint uk_thread_id
            unique,
    user_id            integer      not null
        constraint fk_user_id
            references public.user_list
            on delete cascade,
    title              varchar(50)  not null,
    first_query        text         not null,
    created_at         timestamp with time zone default CURRENT_TIMESTAMP,
    updated_at         timestamp with time zone default CURRENT_TIMESTAMP,
    message_count      integer                  default 1,
    is_deleted         boolean                  default false,
    knowledge_base_id  integer,
    novel_graph_id     integer,
    knowledge_graph_id integer
        constraint fk_chat_threads_knowledge_graph
            references public.graphs
            on delete set null,
    ip                 varchar(128)
);

comment on table public.chat_threads is '聊天会话记录表，记录每个用户的会话基本信息';

comment on column public.chat_threads.id is '主键 ID';

comment on column public.chat_threads.thread_id is '会话线程 ID（UUID 格式）';

comment on column public.chat_threads.user_id is '用户 ID，关联 user_list 表';

comment on column public.chat_threads.title is '会话标题（首次请求内容的前10个字）';

comment on column public.chat_threads.first_query is '首次请求的完整内容';

comment on column public.chat_threads.created_at is '会话创建时间';

comment on column public.chat_threads.updated_at is '最后更新时间';

comment on column public.chat_threads.message_count is '该会话的消息总数';

comment on column public.chat_threads.is_deleted is '是否已删除（软删除标记）';

comment on column public.chat_threads.knowledge_graph_id is '绑定的知识图谱 graphs.id，与 knowledge_base_id 二选一';

comment on column public.chat_threads.ip is '最近一次发起聊天时的客户端 IP（可选）';

alter table public.chat_threads
    owner to zuoleiroot;

create index idx_chat_threads_user_id
    on public.chat_threads (user_id);

create index idx_chat_threads_created_at
    on public.chat_threads (created_at desc);

create index idx_chat_threads_user_created
    on public.chat_threads (user_id asc, created_at desc);

create index idx_chat_threads_novel_graph_id
    on public.chat_threads (novel_graph_id);

create index idx_chat_threads_knowledge_graph_id
    on public.chat_threads (knowledge_graph_id);

create table public.knowledge_processing_task
(
    id                serial
        primary key,
    user_id           integer      not null
        constraint fk_kb_processing_user
            references public.user_list
            on delete cascade,
    knowledge_base_id integer      not null
        constraint fk_kb_processing_kb
            references public.knowledge_base
            on delete cascade,
    task_name         varchar(255) not null,
    instruction       text         not null,
    file_ids          integer[]    not null,
    task_type         varchar(50)  not null,
    status            varchar(20)              default 'pending'::character varying,
    result            text,
    result_file_url   text,
    error_message     text,
    created_at        timestamp with time zone default CURRENT_TIMESTAMP,
    updated_at        timestamp with time zone default CURRENT_TIMESTAMP,
    started_at        timestamp with time zone,
    completed_at      timestamp with time zone
);

comment on table public.knowledge_processing_task is '知识加工任务表：合并、对比、总结等异步任务';

comment on column public.knowledge_processing_task.result_file_url is '加工结果文件的 OSS 下载链接';

alter table public.knowledge_processing_task
    owner to zuoleiroot;

create index idx_kb_processing_user_id
    on public.knowledge_processing_task (user_id);

create index idx_kb_processing_kb_id
    on public.knowledge_processing_task (knowledge_base_id);

create index idx_kb_processing_status
    on public.knowledge_processing_task (status);

create index idx_kb_processing_created_at
    on public.knowledge_processing_task (created_at desc);

create index idx_kb_processing_user_status
    on public.knowledge_processing_task (user_id, status);

create index idx_department_enterprise_id
    on public.department (enterprise_id);

