//! Core types for the BM25F benchmark harness.

use std::path::PathBuf;

use serde::{Deserialize, Serialize};

use rskim_search::{FIELD_COUNT, FileId, SearchField};

/// A single relevance judgment: the file that is the ground-truth for a query.
///
/// Generated by language-specific symbol extractors — the file that defines a
/// symbol is the relevant file for a query of that symbol's name.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Qrel {
    /// The symbol name used as the query text.
    pub query: String,
    /// The `FileId` of the file that defines this symbol (ground truth).
    pub relevant_file_id: FileId,
    /// The field type that produced this symbol.
    pub field: SearchField,
}

/// Metrics for a single configuration on a single corpus.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigMetrics {
    /// Configuration name.
    pub config_name: String,
    /// Mean Reciprocal Rank over all queries.
    pub mrr: f64,
    /// Precision at K=5.
    pub precision_at_5: f64,
    /// Precision at K=10.
    pub precision_at_10: f64,
    /// Number of queries evaluated.
    pub query_count: usize,
    /// Number of queries where the relevant file was found at rank 1.
    pub found_at_rank_1: usize,
}

/// Per-repo benchmark results for all configurations.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RepoBenchResult {
    /// Repository URL.
    pub repo_url: String,
    /// Metrics for each configuration on the train split.
    pub train_metrics: Vec<ConfigMetrics>,
    /// Metrics for each configuration on the test split.
    pub test_metrics: Vec<ConfigMetrics>,
    /// Number of qrels generated for this repo.
    pub qrel_count: usize,
}

/// Full benchmark result across all repos.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchResult {
    /// Per-repo results.
    pub repos: Vec<RepoBenchResult>,
    /// Aggregate train metrics across all repos (macro average).
    pub aggregate_train: Vec<ConfigMetrics>,
    /// Aggregate test metrics across all repos (macro average).
    pub aggregate_test: Vec<ConfigMetrics>,
}

/// A single step in the coordinate descent convergence trace.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConvergenceStep {
    /// Pass number (1-indexed).
    pub pass: usize,
    /// Parameter name being tuned in this step.
    pub parameter: String,
    /// Previous value.
    pub from_value: f64,
    /// New best value found.
    pub to_value: f64,
    /// MRR improvement gained.
    pub mrr_improvement: f64,
}

/// Result of the coordinate descent tuning run.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TuningResult {
    /// Best BM25F configuration found.
    pub best_k1: f32,
    /// Per-field boost weights, one per `SearchField` variant (length == `FIELD_COUNT`).
    pub best_field_boosts: [f32; FIELD_COUNT],
    /// Per-field b parameters, one per `SearchField` variant (length == `FIELD_COUNT`).
    pub best_field_b: [f32; FIELD_COUNT],
    /// MRR achieved by the best config on the train split.
    pub best_train_mrr: f64,
    /// Convergence history.
    pub convergence_history: Vec<ConvergenceStep>,
    /// Number of coordinate descent passes until convergence.
    pub passes_needed: usize,
}

/// An indexed file with its assigned FileId and source path.
#[derive(Debug, Clone)]
pub struct IndexedFile {
    pub file_id: FileId,
    pub path: PathBuf,
    pub language: rskim_core::Language,
}
