-- Momentry 監控系統數據庫表
-- 使用方式: psql -U accusys -h localhost -d momentry -f schema.sql

-- ============================================================
-- Layer 2: Service 監控
-- ============================================================

CREATE TABLE IF NOT EXISTS monitor_services (
    id SERIAL PRIMARY KEY,
    service_name VARCHAR(50) NOT NULL,
    service_type VARCHAR(20),
    port INTEGER,
    status VARCHAR(20) CHECK (status IN ('up', 'down', 'degraded', 'unknown')),
    response_time_ms INTEGER,
    error_message TEXT,
    checked_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_monitor_services_name ON monitor_services(service_name);
CREATE INDEX idx_monitor_services_time ON monitor_services(checked_at);

-- ============================================================
-- Layer 3: n8n Workflow 監控
-- ============================================================

CREATE TABLE IF NOT EXISTS monitor_workflows (
    id SERIAL PRIMARY KEY,
    workflow_id VARCHAR(50) NOT NULL,
    workflow_name VARCHAR(255),
    workflow_type VARCHAR(50),
    is_active BOOLEAN DEFAULT FALSE,
    last_executed_at TIMESTAMP,
    execution_count INTEGER DEFAULT 0,
    success_count INTEGER DEFAULT 0,
    failure_count INTEGER DEFAULT 0,
    avg_duration_ms INTEGER,
    has_schedule BOOLEAN DEFAULT FALSE,
    has_webhook BOOLEAN DEFAULT FALSE,
    idle_days INTEGER,
    suggestion VARCHAR(100),
    checked_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_monitor_workflows_id ON monitor_workflows(workflow_id);
CREATE INDEX idx_monitor_workflows_active ON monitor_workflows(is_active);
CREATE INDEX idx_monitor_workflows_idle ON monitor_workflows(idle_days);

-- ============================================================
-- Layer 4: WordPress Portal 監控
-- ============================================================

CREATE TABLE IF NOT EXISTS monitor_portal_pages (
    id SERIAL PRIMARY KEY,
    page_url VARCHAR(500) NOT NULL,
    page_type VARCHAR(20),
    is_accessible BOOLEAN,
    response_time_ms INTEGER,
    http_status INTEGER,
    error_message TEXT,
    checked_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS monitor_portal_users (
    id SERIAL PRIMARY KEY,
    user_id BIGINT,
    username VARCHAR(100),
    email VARCHAR(255),
    role VARCHAR(50),
    is_active BOOLEAN,
    last_login TIMESTAMP,
    created_at TIMESTAMP,
    detected_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_monitor_portal_pages_url ON monitor_portal_pages(page_url);
CREATE INDEX idx_monitor_portal_users_username ON monitor_portal_users(username);

-- ============================================================
-- Layer 5: Database 監控
-- ============================================================

CREATE TABLE IF NOT EXISTS monitor_databases (
    id SERIAL PRIMARY KEY,
    db_type VARCHAR(20) NOT NULL CHECK (db_type IN ('postgresql', 'redis', 'qdrant', 'mariadb', 'mongodb')),
    db_name VARCHAR(50),
    metric_name VARCHAR(50) NOT NULL,
    metric_value JSONB,
    checked_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_monitor_databases_type ON monitor_databases(db_type);
CREATE INDEX idx_monitor_databases_time ON monitor_databases(checked_at);

-- PostgreSQL 表結構快照
CREATE TABLE IF NOT EXISTS monitor_pg_tables (
    id SERIAL PRIMARY KEY,
    database_name VARCHAR(50),
    schema_name VARCHAR(50),
    table_name VARCHAR(100),
    table_type VARCHAR(20),
    row_count BIGINT,
    table_size_bytes BIGINT,
    index_size_bytes BIGINT,
    snapshot_at TIMESTAMP DEFAULT NOW()
);

-- 表結構變更記錄
CREATE TABLE IF NOT EXISTS monitor_pg_schema_changes (
    id SERIAL PRIMARY KEY,
    database_name VARCHAR(50),
    schema_name VARCHAR(50),
    table_name VARCHAR(100),
    change_type VARCHAR(20) CHECK (change_type IN ('table_created', 'table_dropped', 'column_added', 'column_removed', 'column_type_changed')),
    column_name VARCHAR(100),
    old_value TEXT,
    new_value TEXT,
    detected_at TIMESTAMP DEFAULT NOW()
);

-- Qdrant Collection 監控
CREATE TABLE IF NOT EXISTS monitor_qdrant_collections (
    id SERIAL PRIMARY KEY,
    collection_name VARCHAR(100),
    vectors_count BIGINT,
    points_count BIGINT,
    disk_size_bytes BIGINT,
    status VARCHAR(20),
    snapshot_at TIMESTAMP DEFAULT NOW()
);

-- ============================================================
-- Layer 6: 使用者監控
-- ============================================================

-- 連線會話追蹤
CREATE TABLE IF NOT EXISTS monitor_sessions (
    id SERIAL PRIMARY KEY,
    session_type VARCHAR(20) CHECK (session_type IN ('ssh', 'web', 'db', 'sftp', 'rdp')),
    service_name VARCHAR(50),
    username VARCHAR(100),
    source_ip VARCHAR(45),
    source_port INTEGER,
    connected_at TIMESTAMP,
    last_activity_at TIMESTAMP,
    disconnected_at TIMESTAMP,
    bytes_sent BIGINT,
    bytes_received BIGINT,
    status VARCHAR(20) CHECK (status IN ('active', 'disconnected', 'timeout'))
);

-- 登入歷史
CREATE TABLE IF NOT EXISTS monitor_logins (
    id SERIAL PRIMARY KEY,
    user_type VARCHAR(20) CHECK (user_type IN ('system', 'wordpress', 'n8n', 'gitea', 'sftpgo', 'database')),
    username VARCHAR(100),
    source_ip VARCHAR(45),
    user_agent TEXT,
    login_method VARCHAR(20),
    success BOOLEAN,
    failure_reason VARCHAR(200),
    login_at TIMESTAMP DEFAULT NOW()
);

-- sudo 命令記錄
CREATE TABLE IF NOT EXISTS monitor_sudo_history (
    id SERIAL PRIMARY KEY,
    username VARCHAR(100),
    command TEXT,
    run_as VARCHAR(100),
    tty VARCHAR(50),
    source_ip VARCHAR(45),
    exit_code INTEGER,
    executed_at TIMESTAMP DEFAULT NOW()
);

-- 資源使用追蹤
CREATE TABLE IF NOT EXISTS monitor_resource_usage (
    id SERIAL PRIMARY KEY,
    user_type VARCHAR(20),
    username VARCHAR(100),
    service_name VARCHAR(50),
    cpu_percent DECIMAL(5,2),
    memory_mb INTEGER,
    disk_io_read_mb BIGINT,
    disk_io_write_mb BIGINT,
    network_rx_mb BIGINT,
    network_tx_mb BIGINT,
    recorded_at TIMESTAMP DEFAULT NOW()
);

-- 異常檢測記錄
CREATE TABLE IF NOT EXISTS monitor_anomalies (
    id SERIAL PRIMARY KEY,
    anomaly_type VARCHAR(50) CHECK (anomaly_type IN ('brute_force', 'privilege_escalation', 'unusual_access', 'unusual_time', 'excessive_queries', 'idle_session', 'schema_change')),
    severity VARCHAR(20) CHECK (severity IN ('low', 'medium', 'high', 'critical')),
    source_type VARCHAR(20),
    username VARCHAR(100),
    source_ip VARCHAR(45),
    description TEXT,
    details JSONB,
    detected_at TIMESTAMP DEFAULT NOW(),
    resolved BOOLEAN DEFAULT FALSE,
    resolved_at TIMESTAMP
);

CREATE INDEX idx_monitor_sessions_type ON monitor_sessions(session_type);
CREATE INDEX idx_monitor_sessions_username ON monitor_sessions(username);
CREATE INDEX idx_monitor_logins_type ON monitor_logins(user_type);
CREATE INDEX idx_monitor_logins_time ON monitor_logins(login_at);
CREATE INDEX idx_monitor_anomalies_type ON monitor_anomalies(anomaly_type);
CREATE INDEX idx_monitor_anomalies_severity ON monitor_anomalies(severity);
CREATE INDEX idx_monitor_anomalies_time ON monitor_anomalies(detected_at);

-- ============================================================
-- Layer 7: Storage 監控
-- ============================================================

-- 檔案註冊表
CREATE TABLE IF NOT EXISTS file_registry (
    file_uuid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    file_name VARCHAR(255) NOT NULL,
    file_path TEXT NOT NULL,
    file_path_hash VARCHAR(64) NOT NULL,
    file_size BIGINT NOT NULL,
    file_hash VARCHAR(64),
    mime_type VARCHAR(100),
    user_cluster VARCHAR(50) CHECK (user_cluster IN ('family', 'work', 'wordpress', 'shared', 'system')),
    owner_id VARCHAR(100),
    storage_tier VARCHAR(20) DEFAULT 'hot' CHECK (storage_tier IN ('hot', 'warm', 'cold')),
    storage_location VARCHAR(500),
    status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'temporary', 'archived', 'deleted')),
    is_registered BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    last_accessed_at TIMESTAMP,
    access_count INTEGER DEFAULT 0,
    archived_at TIMESTAMP,
    archive_location VARCHAR(500),
    retention_until TIMESTAMP,
    UNIQUE(file_path_hash)
);

-- 存儲使用統計
CREATE TABLE IF NOT EXISTS storage_usage_stats (
    id SERIAL PRIMARY KEY,
    user_cluster VARCHAR(50),
    storage_tier VARCHAR(20),
    file_count BIGINT,
    total_size_bytes BIGINT,
    record_time TIMESTAMP DEFAULT NOW()
);

-- 文件訪問日誌
CREATE TABLE IF NOT EXISTS storage_access_logs (
    id SERIAL PRIMARY KEY,
    user_cluster VARCHAR(50),
    owner_id VARCHAR(100),
    file_path TEXT,
    access_type VARCHAR(20) CHECK (access_type IN ('read', 'write', 'delete', 'download', 'move')),
    access_time TIMESTAMP DEFAULT NOW(),
    client_ip VARCHAR(45),
    access_method VARCHAR(20)
);

-- 文件生命週期
CREATE TABLE IF NOT EXISTS file_lifecycle (
    id SERIAL PRIMARY KEY,
    file_uuid UUID REFERENCES file_registry(file_uuid),
    file_path TEXT,
    user_cluster VARCHAR(50),
    storage_tier VARCHAR(20),
    created_at TIMESTAMP,
    last_accessed_at TIMESTAMP,
    last_modified_at TIMESTAMP,
    access_count INTEGER DEFAULT 0,
    current_status VARCHAR(20) DEFAULT 'active',
    tier_migration_count INTEGER DEFAULT 0,
    migrated_at TIMESTAMP
);

CREATE INDEX idx_file_registry_cluster ON file_registry(user_cluster);
CREATE INDEX idx_file_registry_tier ON file_registry(storage_tier);
CREATE INDEX idx_file_registry_status ON file_registry(status);
CREATE INDEX idx_storage_usage_cluster ON storage_usage_stats(user_cluster);
CREATE INDEX idx_storage_usage_time ON storage_usage_stats(record_time);

-- ============================================================
-- 外部監控 (Layer 1)
-- ============================================================

CREATE TABLE IF NOT EXISTS monitor_external (
    id SERIAL PRIMARY KEY,
    target_name VARCHAR(50) NOT NULL,
    target_type VARCHAR(20) CHECK (target_type IN ('ddns', 'gateway', 'internet', 'api')),
    target_host VARCHAR(255),
    is_reachable BOOLEAN,
    response_time_ms INTEGER,
    dns_resolved_ip VARCHAR(45),
    error_message TEXT,
    checked_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_monitor_external_name ON monitor_external(target_name);
CREATE INDEX idx_monitor_external_time ON monitor_external(checked_at);

-- ============================================================
-- 監控配置表
-- ============================================================

CREATE TABLE IF NOT EXISTS monitor_config (
    id SERIAL PRIMARY KEY,
    config_key VARCHAR(50) UNIQUE NOT NULL,
    config_value TEXT,
    description VARCHAR(255),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- 插入默認配置
INSERT INTO monitor_config (config_key, config_value, description) VALUES
    ('check_interval', '300', '監控檢查間隔（秒）'),
    ('retention_days', '30', '歷史數據保留天數'),
    ('idle_threshold_days', '30', 'Workflow 閒置天數閾值'),
    ('alert_threshold_bruteforce', '5', '暴力破解嘗試次數閾值'),
    ('alert_threshold_slow_response', '3000', '響應時間閾值（毫秒）')
ON CONFLICT (config_key) DO NOTHING;

-- ============================================================
-- 視圖定義
-- ============================================================

-- 服務健康狀態視圖
CREATE OR REPLACE VIEW v_service_health AS
SELECT 
    service_name,
    status,
    COUNT(*) as check_count,
    COUNT(*) FILTER (WHERE status = 'up') as up_count,
    COUNT(*) FILTER (WHERE status = 'down') as down_count,
    AVG(response_time_ms) as avg_response_time,
    MAX(checked_at) as last_check
FROM monitor_services
WHERE checked_at > NOW() - INTERVAL '24 hours'
GROUP BY service_name, status;

-- 最近異常視圖
CREATE OR REPLACE VIEW v_recent_anomalies AS
SELECT 
    anomaly_type,
    severity,
    username,
    source_ip,
    description,
    detected_at
FROM monitor_anomalies
WHERE detected_at > NOW() - INTERVAL '24 hours'
ORDER BY detected_at DESC;

-- 閒置 Workflow 視圖
CREATE OR REPLACE VIEW v_idle_workflows AS
SELECT 
    workflow_name,
    idle_days,
    suggestion,
    last_executed_at
FROM monitor_workflows
WHERE idle_days > 30 AND is_active = TRUE
ORDER BY idle_days DESC;

-- 存儲使用概況視圖
CREATE OR REPLACE VIEW v_storage_overview AS
SELECT 
    user_cluster,
    storage_tier,
    COUNT(*) as file_count,
    SUM(file_size) as total_size
FROM file_registry
WHERE status = 'active'
GROUP BY user_cluster, storage_tier;

-- ============================================================
-- 備份監控 (Layer 7 Extension)
-- ============================================================

-- 備份註冊表
CREATE TABLE IF NOT EXISTS backup_registry (
    id SERIAL PRIMARY KEY,
    service_name VARCHAR(50) NOT NULL,
    backup_file VARCHAR(500) NOT NULL,
    backup_size_bytes BIGINT,
    backup_type VARCHAR(20) CHECK (backup_type IN ('daily', 'weekly', 'monthly', 'archive', 'full', 'incremental')),
    backup_method VARCHAR(20) CHECK (backup_method IN ('pg_dump', 'mysqldump', 'tar', 'snapshot', 'dump')),
    status VARCHAR(20) CHECK (status IN ('pending', 'running', 'completed', 'failed', 'verified')),
    compression_ratio DECIMAL(5,2),
    verification_result BOOLEAN,
    error_message TEXT,
    started_at TIMESTAMP DEFAULT NOW(),
    completed_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW()
);

-- 備份存儲統計
CREATE TABLE IF NOT EXISTS backup_storage_stats (
    id SERIAL PRIMARY KEY,
    tier VARCHAR(20) CHECK (tier IN ('daily', 'weekly', 'monthly', 'archive', 'total')),
    file_count BIGINT,
    total_size_bytes BIGINT,
    record_time TIMESTAMP DEFAULT NOW()
);

-- 備份歷史
CREATE TABLE IF NOT EXISTS backup_history (
    id SERIAL PRIMARY KEY,
    service_name VARCHAR(50) NOT NULL,
    operation VARCHAR(20) CHECK (operation IN ('backup', 'restore', 'tier_migration', 'cleanup', 'verify')),
    backup_file VARCHAR(500),
    backup_tier VARCHAR(20),
    source_tier VARCHAR(20),
    dest_tier VARCHAR(20),
    file_count BIGINT,
    size_bytes BIGINT,
    duration_seconds INTEGER,
    status VARCHAR(20) CHECK (status IN ('success', 'failed', 'partial')),
    error_message TEXT,
    executed_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_backup_registry_service ON backup_registry(service_name);
CREATE INDEX idx_backup_registry_time ON backup_registry(created_at);
CREATE INDEX idx_backup_storage_stats_tier ON backup_storage_stats(tier);
CREATE INDEX idx_backup_storage_stats_time ON backup_storage_stats(record_time);
CREATE INDEX idx_backup_history_service ON backup_history(service_name);
CREATE INDEX idx_backup_history_time ON backup_history(executed_at);

-- ============================================================
-- Node.js 版本基線監控
-- ============================================================

CREATE TABLE IF NOT EXISTS node_version_baseline (
    id SERIAL PRIMARY KEY,
    runtime_name VARCHAR(50) NOT NULL,
    required_version VARCHAR(20) NOT NULL,
    current_version VARCHAR(20),
    process_name VARCHAR(100),
    process_path TEXT,
    is_compliant BOOLEAN,
    locked_path VARCHAR(500),
    checked_at TIMESTAMP DEFAULT NOW()
);

-- Node.js 進程追蹤
CREATE TABLE IF NOT EXISTS node_process_tracking (
    id SERIAL PRIMARY KEY,
    process_name VARCHAR(100) NOT NULL,
    pid INTEGER,
    command VARCHAR(500),
    node_version VARCHAR(20),
    is_managed BOOLEAN DEFAULT FALSE,
    started_at TIMESTAMP,
    checked_at TIMESTAMP DEFAULT NOW()
);

-- ============================================================
-- Python 版本基線監控
-- ============================================================

CREATE TABLE IF NOT EXISTS python_version_baseline (
    id SERIAL PRIMARY KEY,
    runtime_name VARCHAR(50) NOT NULL,
    required_version VARCHAR(20) NOT NULL,
    current_version VARCHAR(20),
    interpreter_path VARCHAR(500),
    is_compliant BOOLEAN,
    checked_at TIMESTAMP DEFAULT NOW()
);

-- Python 腳本追蹤
CREATE TABLE IF NOT EXISTS python_script_tracking (
    id SERIAL PRIMARY KEY,
    script_path TEXT NOT NULL,
    shebang_version VARCHAR(20),
    actual_version VARCHAR(20),
    is_compliant BOOLEAN DEFAULT FALSE,
    last_run_at TIMESTAMP,
    exit_code INTEGER,
    error_output TEXT,
    checked_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_node_version_name ON node_version_baseline(runtime_name);
CREATE INDEX idx_node_process_name ON node_process_tracking(process_name);
CREATE INDEX idx_python_version_name ON python_version_baseline(runtime_name);
CREATE INDEX idx_python_script_path ON python_script_tracking(script_path);
