//! `BotConfig` struct and serde default functions for `bot.toml` deserialization.
use serde::Deserialize;

pub(super) fn default_history_size() -> usize {
    20
}

pub(super) fn default_permission_timeout_secs() -> u64 {
    120
}

/// Default coalesce window for the chat dispatcher (1 500 ms).
pub(super) fn default_coalesce_window_ms() -> u64 {
    1_500
}

/// Default cap (bytes) on the digest the `compact` command writes as a seed.
pub(super) fn default_compact_seed_max_bytes() -> usize {
    8_000
}

/// Default cache_read token threshold above which the bot suggests running
/// `compact` after a turn.
pub(super) fn default_cache_read_suggest_threshold() -> u64 {
    50_000
}

/// Default cooldown (seconds) between repeated `compact` suggestions for the
/// same room.
pub(super) fn default_compact_suggest_cooldown_secs() -> i64 {
    3_600
}

pub(super) fn default_transport() -> String {
    "matrix".to_string()
}

pub(super) fn default_whatsapp_provider() -> String {
    "meta".to_string()
}

/// Configuration for the Matrix bot, read from `.huskies/bot.toml`.
#[derive(Deserialize, Clone, Debug)]
pub struct BotConfig {
    /// Matrix homeserver URL, e.g. `https://matrix.example.com`
    /// Only required when `transport = "matrix"` (the default).
    #[serde(default)]
    pub homeserver: Option<String>,
    /// Bot user ID, e.g. `@storykit:example.com`
    /// Only required when `transport = "matrix"`.
    #[serde(default)]
    pub username: Option<String>,
    /// Bot password
    /// Only required when `transport = "matrix"`.
    #[serde(default)]
    pub password: Option<String>,
    /// Matrix room IDs to join, e.g. `["!roomid:example.com"]`.
    /// Use an array for multiple rooms; a single string is accepted via the
    /// deprecated `room_id` key for backwards compatibility.
    #[serde(default)]
    pub room_ids: Vec<String>,
    /// Deprecated: use `room_ids` (list) instead. Still accepted so existing
    /// `bot.toml` files continue to work without modification.
    #[serde(default)]
    pub room_id: Option<String>,
    /// Set to `true` to enable the bot (default: false)
    #[serde(default)]
    pub enabled: bool,
    /// Matrix user IDs allowed to interact with the bot.
    /// If empty or omitted, the bot ignores ALL messages (fail-closed).
    #[serde(default)]
    pub allowed_users: Vec<String>,
    /// Maximum number of conversation turns (user + assistant pairs) to keep
    /// per room. When the history exceeds this limit the oldest messages are
    /// dropped. Defaults to 20.
    #[serde(default = "default_history_size")]
    pub history_size: usize,
    /// Timeout in seconds for permission prompts surfaced to the Matrix room.
    /// If the user does not respond within this window the permission is denied
    /// (fail-closed). Defaults to 120 seconds.
    #[serde(default = "default_permission_timeout_secs")]
    pub permission_timeout_secs: u64,
    /// Claude Code model override. When set, passed as `--model <value>` to
    /// the `claude` CLI so the bot uses a specific model instead of the CLI's
    /// configured default.
    #[serde(default)]
    pub model: Option<String>,
    /// Display name the bot uses to identify itself in conversations.
    /// If unset, the bot falls back to "Assistant".
    #[serde(default)]
    pub display_name: Option<String>,
    /// Room IDs where ambient mode is active (bot responds to all messages).
    /// Updated at runtime when the user toggles ambient mode — do not edit
    /// manually while the bot is running.
    #[serde(default)]
    pub ambient_rooms: Vec<String>,
    /// Chat transport to use: `"matrix"` (default), `"whatsapp"`, `"slack"`,
    /// or `"discord"`.
    ///
    /// Selects which [`ChatTransport`] implementation the bot uses for
    /// sending and editing messages.  Currently only read during bot
    /// startup to select the transport; the field is kept for config
    /// round-tripping.
    #[serde(default = "default_transport")]
    pub transport: String,

    // ── WhatsApp Business API fields ─────────────────────────────────
    // These are only required when `transport = "whatsapp"`.
    /// WhatsApp Business phone number ID from the Meta dashboard.
    #[serde(default)]
    pub whatsapp_phone_number_id: Option<String>,
    /// Long-lived access token for the WhatsApp Business API.
    #[serde(default)]
    pub whatsapp_access_token: Option<String>,
    /// Verify token used in the webhook handshake (you choose this value
    /// and configure it in the Meta webhook settings).
    #[serde(default)]
    pub whatsapp_verify_token: Option<String>,
    /// Name of the approved Meta message template used for pipeline
    /// notifications when the 24-hour messaging window has expired.
    ///
    /// The template must be registered in the Meta Business Manager before
    /// use.  Defaults to `"pipeline_notification"`.
    #[serde(default)]
    pub whatsapp_notification_template: Option<String>,
    /// Which WhatsApp provider to use: `"meta"` (default, direct Graph API)
    /// or `"twilio"` (Twilio REST API as alternative to Meta).
    ///
    /// When `"twilio"`, the Twilio-specific fields below are required instead
    /// of the Meta `whatsapp_phone_number_id` / `whatsapp_access_token` pair.
    #[serde(default = "default_whatsapp_provider")]
    pub whatsapp_provider: String,

    // ── Twilio WhatsApp fields ─────────────────────────────────────────
    // Only required when `transport = "whatsapp"` and `whatsapp_provider = "twilio"`.
    /// Twilio Account SID (starts with `AC`).
    #[serde(default)]
    pub twilio_account_sid: Option<String>,
    /// Twilio Auth Token.
    #[serde(default)]
    pub twilio_auth_token: Option<String>,
    /// Twilio WhatsApp sender number in E.164 format, e.g. `+14155551234`.
    #[serde(default)]
    pub twilio_whatsapp_number: Option<String>,

    /// Phone numbers allowed to interact with the bot when using WhatsApp.
    /// When non-empty, only listed numbers can send commands; all others are
    /// silently ignored. When empty or absent, all numbers are allowed
    /// (backwards compatible — open by default, unlike Matrix which is
    /// fail-closed).
    #[serde(default)]
    pub whatsapp_allowed_phones: Vec<String>,
    /// Meta app secret used to verify `X-Hub-Signature-256` HMAC on inbound
    /// webhook requests.  When set, every POST to `/webhook/whatsapp` must
    /// carry a valid signature or the request is rejected.  When absent or
    /// empty, signature verification is skipped (backwards compatible).
    #[serde(default)]
    pub whatsapp_app_secret: Option<String>,

    // ── Slack Bot API fields ─────────────────────────────────────────
    // These are only required when `transport = "slack"`.
    /// Slack Bot User OAuth Token (starts with `xoxb-`).
    #[serde(default)]
    pub slack_bot_token: Option<String>,
    /// Slack Signing Secret used to verify incoming webhook requests.
    #[serde(default)]
    pub slack_signing_secret: Option<String>,
    /// Slack channel IDs the bot should listen in.
    #[serde(default)]
    pub slack_channel_ids: Vec<String>,

    // ── Discord Bot API fields ──────────────────────────────────────
    // These are only required when `transport = "discord"`.
    /// Discord bot token from the Discord Developer Portal.
    #[serde(default)]
    pub discord_bot_token: Option<String>,
    /// Discord channel IDs the bot should listen in.
    #[serde(default)]
    pub discord_channel_ids: Vec<String>,
    /// Discord user IDs allowed to interact with the bot.
    /// When empty or absent, all users in configured channels are allowed.
    #[serde(default)]
    pub discord_allowed_users: Vec<String>,

    /// Duration in milliseconds of the chat dispatcher's coalesce window.
    ///
    /// Messages for the same session arriving within this window are
    /// concatenated into a single `claude -p` call.  The window is a
    /// debounce: each new message extends the deadline by this duration.
    ///
    /// Defaults to 1 500 ms (1.5 s).
    #[serde(default = "default_coalesce_window_ms")]
    pub coalesce_window_ms: u64,

    /// Git `user.name` to inject into project containers created by `new project`.
    ///
    /// Passed as `GIT_USER_NAME` to the container entrypoint so agents can commit
    /// code with the correct author identity.  Falls back to the host's
    /// `git config user.name` when absent.
    #[serde(default)]
    pub git_user_name: Option<String>,

    /// Git `user.email` to inject into project containers created by `new project`.
    ///
    /// Passed as `GIT_USER_EMAIL` to the container entrypoint so agents can commit
    /// code with the correct author identity.  Falls back to the host's
    /// `git config user.email` when absent.
    #[serde(default)]
    pub git_user_email: Option<String>,

    /// Maximum size in bytes of the digest the `compact` command writes as a
    /// seed file. Older content is dropped first so the most recent
    /// conversation survives the cap. Defaults to 8000 bytes.
    #[serde(default = "default_compact_seed_max_bytes")]
    pub compact_seed_max_bytes: usize,

    /// Number of `cache_read_input_tokens` in a single turn above which the
    /// bot suggests running `compact`. Defaults to 50 000.
    #[serde(default = "default_cache_read_suggest_threshold")]
    pub cache_read_suggest_threshold: u64,

    /// Minimum seconds between repeated `compact` suggestions for the same
    /// room, so a busy room isn't spammed every turn. Defaults to 3600 (1h).
    #[serde(default = "default_compact_suggest_cooldown_secs")]
    pub compact_suggest_cooldown_secs: i64,
}
