//! SQLite 控制面 Phase 1 的最小数据模型。

use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

pub type EntityId = String;
pub type Timestamp = String;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UserRecord {
    pub id: EntityId,
    pub email: Option<String>,
    pub username: String,
    pub display_name: String,
    pub role: String,
    pub status: String,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

pub fn session_token_hash(raw_token: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(raw_token.as_bytes());
    hex::encode(hasher.finalize())
}

pub fn password_hash_v1(password: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(b"mnote-password-v1:");
    hasher.update(password.as_bytes());
    format!("sha256-v1:{}", hex::encode(hasher.finalize()))
}

pub fn share_token_hash_v1(token: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(b"mnote-share-token-v1:");
    hasher.update(token.as_bytes());
    format!("sha256-v1:{}", hex::encode(hasher.finalize()))
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpsertUserInput {
    pub id: Option<EntityId>,
    pub email: Option<String>,
    pub username: String,
    pub display_name: String,
    pub role: Option<String>,
    pub password_hash: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreatePasswordIdentityInput {
    pub user_id: EntityId,
    pub email: Option<String>,
    pub username: String,
    pub password: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthenticatePasswordInput {
    pub account: String,
    pub password: String,
    pub session_id: Option<EntityId>,
    pub token_hash: String,
    pub user_agent: Option<String>,
    pub ip_hash: Option<String>,
    pub expires_at: Option<Timestamp>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthSessionRecord {
    pub id: EntityId,
    pub user_id: EntityId,
    pub token_hash: String,
    pub user_agent: Option<String>,
    pub ip_hash: Option<String>,
    pub created_at: Timestamp,
    pub expires_at: Timestamp,
    pub revoked_at: Option<Timestamp>,
    pub last_seen_at: Timestamp,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreateSessionInput {
    pub id: Option<EntityId>,
    pub user_id: EntityId,
    pub token_hash: String,
    pub user_agent: Option<String>,
    pub ip_hash: Option<String>,
    pub expires_at: Option<Timestamp>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResolvedAuthSession {
    pub session: AuthSessionRecord,
    pub user: UserRecord,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WorkspaceRecord {
    pub id: EntityId,
    pub owner_user_id: EntityId,
    pub name: String,
    pub kind: String,
    pub root_uri: String,
    pub root_path: String,
    pub source_kind: String,
    pub status: String,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirectoryGrantRecord {
    pub id: EntityId,
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub root_uri: String,
    pub root_path: String,
    pub permission: String,
    pub recursive: bool,
    pub capabilities_json: String,
    pub source: String,
    pub status: String,
    pub created_by: Option<EntityId>,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirectoryGrantLookup {
    pub grant_id: Option<EntityId>,
    pub user_id: Option<EntityId>,
    pub root_uri: Option<String>,
    pub include_revoked: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirectoryGrantInput {
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub root_uri: String,
    pub root_path: String,
    pub permission: String,
    pub recursive: bool,
    pub capabilities: Vec<String>,
    pub source: String,
    pub created_by: Option<EntityId>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResolvedAccess {
    pub user_id: EntityId,
    pub root_uri: String,
    pub permission: String,
    pub grant_ids: Vec<EntityId>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OutboxEventRecord {
    pub id: EntityId,
    pub topic: String,
    pub event_type: String,
    pub payload_json: String,
    pub created_at: Timestamp,
    pub delivered_at: Option<Timestamp>,
    pub attempts: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OutboxEventInput {
    pub topic: String,
    pub event_type: String,
    pub payload_json: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SidebarShortcutRecord {
    pub id: EntityId,
    pub user_id: EntityId,
    pub workspace_id: EntityId,
    pub root_uri: Option<String>,
    pub kind: String,
    pub source_kind: String,
    pub target_id: String,
    pub relative_path: Option<String>,
    pub document_id: Option<String>,
    pub title: String,
    pub icon: Option<String>,
    pub sort_order: i64,
    pub status: String,
    pub metadata_json: String,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpsertSidebarShortcutInput {
    pub id: Option<EntityId>,
    pub user_id: EntityId,
    pub workspace_id: EntityId,
    pub root_uri: Option<String>,
    pub kind: String,
    pub source_kind: String,
    pub target_id: String,
    pub relative_path: Option<String>,
    pub document_id: Option<String>,
    pub title: String,
    pub icon: Option<String>,
    pub sort_order: i64,
    pub metadata_json: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UserUiPreferenceRecord {
    pub id: EntityId,
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub source_kind: Option<String>,
    pub scope_kind: String,
    pub scope_id: String,
    pub key: String,
    pub value_json: String,
    pub status: String,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpsertUserUiPreferenceInput {
    pub id: Option<EntityId>,
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub source_kind: Option<String>,
    pub scope_kind: String,
    pub scope_id: String,
    pub key: String,
    pub value_json: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AiPolicyRecord {
    pub id: EntityId,
    pub user_id: Option<EntityId>,
    pub workspace_id: Option<EntityId>,
    pub allowed_roots_json: String,
    pub model_policy_json: String,
    pub quota_json: String,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpsertAiPolicyInput {
    pub id: Option<EntityId>,
    pub user_id: Option<EntityId>,
    pub workspace_id: Option<EntityId>,
    pub allowed_roots_json: String,
    pub model_policy_json: String,
    pub quota_json: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SyncStateRecord {
    pub id: EntityId,
    pub workspace_id: EntityId,
    pub remote_kind: String,
    pub remote_id: Option<String>,
    pub cursor: Option<String>,
    pub last_synced_at: Option<Timestamp>,
    pub status: String,
    pub error_json: Option<String>,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpsertSyncStateInput {
    pub id: Option<EntityId>,
    pub workspace_id: EntityId,
    pub remote_kind: String,
    pub remote_id: Option<String>,
    pub cursor: Option<String>,
    pub last_synced_at: Option<Timestamp>,
    pub status: String,
    pub error_json: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AiRuntimeRunRecord {
    pub id: EntityId,
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub document_id: Option<String>,
    pub session_id: EntityId,
    pub run_id: EntityId,
    pub title: Option<String>,
    pub profile: String,
    pub acp_runtime: String,
    pub trace_id: Option<String>,
    pub status: String,
    pub runtime_json: String,
    pub payload_json: String,
    pub deleted_at: Option<Timestamp>,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpsertAiRuntimeRunInput {
    pub id: Option<EntityId>,
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub document_id: Option<String>,
    pub session_id: EntityId,
    pub run_id: EntityId,
    pub title: Option<String>,
    pub profile: String,
    pub acp_runtime: String,
    pub trace_id: Option<String>,
    pub status: String,
    pub runtime_json: String,
    pub payload_json: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AiRuntimeEventRecord {
    pub id: EntityId,
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub document_id: Option<String>,
    pub session_id: EntityId,
    pub run_id: EntityId,
    pub profile: String,
    pub acp_runtime: String,
    pub event_type: String,
    pub payload_json: String,
    pub created_at: Timestamp,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AppendAiRuntimeEventInput {
    pub id: Option<EntityId>,
    pub user_id: EntityId,
    pub workspace_id: Option<EntityId>,
    pub document_id: Option<String>,
    pub session_id: EntityId,
    pub run_id: EntityId,
    pub profile: String,
    pub acp_runtime: String,
    pub event_type: String,
    pub payload_json: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ShareLinkRecord {
    pub id: EntityId,
    pub workspace_id: EntityId,
    pub resource_kind: String,
    pub resource_id: String,
    pub token_hash: String,
    pub permission: String,
    pub created_by: EntityId,
    pub expires_at: Option<Timestamp>,
    pub revoked_at: Option<Timestamp>,
    pub created_at: Timestamp,
    pub updated_at: Timestamp,
    pub revision: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreateShareLinkInput {
    pub id: Option<EntityId>,
    pub workspace_id: EntityId,
    pub resource_kind: String,
    pub resource_id: String,
    pub token: Option<String>,
    pub permission: String,
    pub created_by: EntityId,
    pub expires_at: Option<Timestamp>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreatedShareLink {
    pub link: ShareLinkRecord,
    pub token: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditLogRecord {
    pub id: EntityId,
    pub actor_user_id: Option<EntityId>,
    pub action: String,
    pub target_kind: String,
    pub target_id: Option<EntityId>,
    pub metadata_json: String,
    pub created_at: Timestamp,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AppendAuditInput {
    pub actor_user_id: Option<EntityId>,
    pub action: String,
    pub target_kind: String,
    pub target_id: Option<EntityId>,
    pub metadata_json: String,
}
