//! Tracing-based logging setup.
//!
//! TUI applications cannot write logs to stdout/stderr without
//! corrupting the terminal, so logs go to a file. The path comes
//! from the CLI (`--log-file`) or the `RDBMS_PLAYGROUND_LOG_FILE`
//! environment variable; if neither is set we default to
//! `~/.rdbms-playground/playground.log` and create directories as
//! needed.
//!
//! ## Level conventions (X1 — `requirements.md`)
//!
//! Instrumentation across the tree follows a consistent level
//! discipline so the default `info` filter stays quiet and
//! `RDBMS_PLAYGROUND_LOG=debug` (or `=trace`) is a rich, layered
//! diagnostic stream. The env filter (`RDBMS_PLAYGROUND_LOG`,
//! full `EnvFilter` syntax) controls this independently of the
//! file path above; the default is `info`.
//!
//! - **`error!`** — unrecoverable failure (fatal persistence, a
//!   panic-equivalent). The process is going down or a command is
//!   hard-failing.
//! - **`warn!`** — recoverable failure or a fallback taken (a
//!   snapshot couldn't be staged, a `PRAGMA` couldn't be restored,
//!   an integrity check rolled a rebuild back).
//! - **`info!`** — low-volume lifecycle, visible by default: db
//!   worker start/exit, project create/open, "logging initialised".
//! - **`debug!`** — the bulk of instrumentation, one line per
//!   *executed* command and the decision points within it (executor
//!   entry with key params, autofill/cascade summaries, the
//!   rebuild-table primitive, persistence writes, render-mode
//!   choice). Off by default.
//! - **`trace!`** — hot paths only: per-keystroke parsing
//!   (`dsl::parser`), per-key input handling (`app`), per-refresh
//!   table reads. A firehose; never on except when debugging that
//!   specific layer.
//!
//! Rule of thumb for new code: a loop logs a single summary count,
//! never per-iteration at `debug`/`info`. Logs are developer-facing,
//! so naming the engine (SQLite/PRAGMA) is fine here even though the
//! "no engine name" rule (ADR-0002) forbids it in user-facing strings.

use std::fs::{File, OpenOptions, create_dir_all};
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};

const DEFAULT_LOG_DIR: &str = ".rdbms-playground";
const DEFAULT_LOG_FILE: &str = "playground.log";

/// Initialise tracing to write to the given file path, or to a
/// platform-default path when `path` is `None`.
pub fn init(path: Option<&Path>) -> Result<PathBuf> {
    let chosen = match path {
        Some(p) => p.to_path_buf(),
        None => default_log_path()?,
    };
    if let Some(parent) = chosen.parent() {
        create_dir_all(parent)
            .with_context(|| format!("create log directory {}", parent.display()))?;
    }
    let file = open_log_file(&chosen)?;
    let filter =
        EnvFilter::try_from_env("RDBMS_PLAYGROUND_LOG").unwrap_or_else(|_| EnvFilter::new("info"));
    let layer = fmt::layer()
        .with_writer(file)
        .with_ansi(false)
        .with_target(true);
    tracing_subscriber::registry()
        .with(filter)
        .with(layer)
        .init();
    tracing::info!(path = %chosen.display(), "logging initialised");
    Ok(chosen)
}

fn open_log_file(path: &Path) -> Result<File> {
    OpenOptions::new()
        .create(true)
        .append(true)
        .open(path)
        .with_context(|| format!("open log file {}", path.display()))
}

fn default_log_path() -> Result<PathBuf> {
    let home = home_dir().context("could not determine HOME directory for default log path")?;
    Ok(home.join(DEFAULT_LOG_DIR).join(DEFAULT_LOG_FILE))
}

fn home_dir() -> Option<PathBuf> {
    // std::env::home_dir is deprecated; do the lookup ourselves with
    // the platform-conventional environment variables. Once we add a
    // proper path strategy (project storage, ADR-0004) this can be
    // replaced with the chosen helper crate.
    if let Some(p) = std::env::var_os("HOME") {
        return Some(PathBuf::from(p));
    }
    if let (Some(drive), Some(path)) = (std::env::var_os("HOMEDRIVE"), std::env::var_os("HOMEPATH"))
    {
        let mut combined = PathBuf::from(drive);
        combined.push(path);
        return Some(combined);
    }
    std::env::var_os("USERPROFILE").map(PathBuf::from)
}
