use serde::{Deserialize, Serialize};
#[cfg(feature = "community")]
use crate::utils::lsa::LsaModel;
use crate::utils::tokenizer::JapaneseTokenizer;
use hnsw_rs::prelude::*;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicU64};
use std::sync::Arc;
use tokio::sync::{broadcast, mpsc, RwLock};

/// フォルダ監視の設定（パス一覧と取込対象拡張子）。設定保存時にワッチャーへ送る（計画 folder_monitor）。
#[derive(Clone, Default)]
pub struct WatcherConfig {
    pub paths: Vec<PathBuf>,
    pub extensions: Vec<String>,
    /// モニター先パス → カテゴリ名のマッピング
    pub category_map: std::collections::HashMap<PathBuf, String>,
}

/// 設定保存時にワッチャーを再起動する用（Phase 3）
pub type WatcherRestartSender = std::sync::mpsc::Sender<WatcherConfig>;

/// monitor_paths JSON 配列をパースする。オブジェクト配列 `[{path, category}]` と旧形式の文字列配列 `["path"]` の両方に対応。
pub fn parse_monitor_paths(arr: &[serde_json::Value]) -> (Vec<PathBuf>, std::collections::HashMap<PathBuf, String>) {
    let mut paths = Vec::new();
    let mut category_map = std::collections::HashMap::new();
    for v in arr {
        if let Some(obj) = v.as_object() {
            if let Some(p) = obj.get("path").and_then(|x| x.as_str()) {
                let pb = PathBuf::from(p);
                let cat = obj.get("category").and_then(|x| x.as_str()).unwrap_or("").to_string();
                if !cat.is_empty() {
                    category_map.insert(pb.clone(), cat);
                }
                paths.push(pb);
            }
        } else if let Some(s) = v.as_str() {
            paths.push(PathBuf::from(s));
        }
    }
    (paths, category_map)
}

#[derive(Clone)]
pub struct AppState {
    pub app_data_dir: PathBuf,
    pub db_pool: sqlx::SqlitePool,
    pub tx: broadcast::Sender<String>,
    pub model_name: String,
    /// エディション: "community" | "pro"（GUI 表示の切り替え用）
    pub edition: String,
    // MCP sessions map
    pub sessions: Arc<RwLock<HashMap<String, mpsc::UnboundedSender<String>>>>,
    // Japanese NLP & LSA (Community 版)
    pub tokenizer: Arc<JapaneseTokenizer>,
    #[cfg(feature = "community")]
    pub lsa_model: Arc<RwLock<Option<LsaModel>>>,
    #[cfg(feature = "pro")]
    pub embedding_model: Arc<RwLock<Option<crate::utils::embedding_pro::ProEmbeddingModel>>>,
    /// Pro で HNSW が空のときに検索から 1 回だけ遅延構築を試みたか
    #[cfg(feature = "pro")]
    pub pro_hnsw_rebuild_requested: Arc<AtomicBool>,
    pub hnsw_index: Arc<RwLock<Option<Hnsw<'static, f32, DistCosine>>>>,
    /// 前回の LSA 再学習以降の変更件数（追加・更新・削除）。この値が閾値を超えたら自動で再学習をスケジュールする。
    pub changes_since_train: Arc<AtomicU64>,
    /// 再学習がスケジュール済みか（二重実行防止）
    pub retrain_scheduled: Arc<AtomicBool>,
    /// インデックス化の状態（idle / training / syncing）。GUI 表示用。
    pub indexing_status: Arc<RwLock<String>>,
    /// フォルダ監視の取込/削除中の表示用（例: "取込中: file.txt"）。同期スレッドから更新するため std::sync::RwLock。
    pub watch_ingestion_status: Arc<std::sync::RwLock<String>>,
    /// 設定保存時にモニター先パスを送るとワッチャーが再起動する（Phase 3）
    pub watcher_restart_tx: Option<WatcherRestartSender>,
}

#[derive(Serialize, Deserialize)]
pub struct JsonRpcRequest {
    pub jsonrpc: String,
    pub method: String,
    pub params: Option<serde_json::Value>,
    pub id: Option<serde_json::Value>,
}

#[derive(Serialize)]
pub struct JsonRpcResponse {
    pub jsonrpc: &'static str,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub result: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<serde_json::Value>,
    pub id: Option<serde_json::Value>,
}

#[derive(Deserialize)]
pub struct MessageQuery {
    #[serde(alias = "sessionId")]
    pub session_id: Option<String>,
}
