//! DTOs for Source Registry operations.
//!
//! These DTOs expose source metadata via the REST API, allowing developers
//! to register sources with human-readable labels and retrieve them by hash.

use serde::{Deserialize, Serialize};
use stemedb_core::types::SourceRecord;
use utoipa::ToSchema;

/// Request to register a new source in the registry.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RegisterSourceRequest {
    /// Hash of the source content (hex-encoded, 64 chars / 32 bytes).
    /// This should match the hash returned from POST /v1/source.
    pub hash: String,

    /// Human-readable label for the source.
    /// Examples: "RFC 7519 - JSON Web Token", "FDA Approval Letter for Wegovy"
    pub label: String,

    /// Optional URL where the source can be accessed.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    /// Authority tier (0-5).
    /// 0 = Regulatory, 1 = Clinical, 2 = Observational,
    /// 3 = Expert, 4 = Community, 5 = Anecdotal
    #[schema(minimum = 0, maximum = 5)]
    pub tier: u8,

    /// Optional curator notes about the source.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub notes: Option<String>,
}

/// Response from registering a source.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RegisterSourceResponse {
    /// Hash of the registered source (hex-encoded).
    pub hash: String,

    /// Human-readable label.
    pub label: String,

    /// Status message.
    pub status: String,
}

/// Source registry data transfer object.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SourceRecordDto {
    /// Hash of the source (hex-encoded, 64 chars / 32 bytes).
    pub hash: String,

    /// Human-readable label for the source.
    pub label: String,

    /// Optional URL where the source can be accessed.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    /// Authority tier (0-5).
    #[schema(minimum = 0, maximum = 5)]
    pub tier: u8,

    /// Human-readable tier label (e.g., "Regulatory", "Clinical").
    pub tier_label: String,

    /// Current status (active, deprecated, quarantined).
    pub status: String,

    /// Unix timestamp when created.
    pub created_at: u64,

    /// Unix timestamp of last update.
    pub updated_at: u64,

    /// Optional curator notes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub notes: Option<String>,
}

impl From<SourceRecord> for SourceRecordDto {
    fn from(record: SourceRecord) -> Self {
        Self {
            hash: hex::encode(record.hash),
            label: record.label.clone(),
            url: record.url.clone(),
            tier: record.tier,
            tier_label: record.tier_label().to_string(),
            status: record.status.as_str().to_string(),
            created_at: record.created_at,
            updated_at: record.updated_at,
            notes: record.notes.clone(),
        }
    }
}

/// Request to update source status.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UpdateSourceStatusRequest {
    /// New status: "active", "deprecated", or "quarantined".
    pub status: String,
}

/// Response from updating source status.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UpdateSourceStatusResponse {
    /// Hash of the source (hex-encoded).
    pub hash: String,

    /// New status.
    pub status: String,

    /// Status message.
    pub message: String,
}

/// Query parameters for listing sources.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
pub struct ListSourcesParams {
    /// Filter by tier (optional).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tier: Option<u8>,

    /// Search by label prefix (optional, case-insensitive).
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "q")]
    pub query: Option<String>,

    /// Maximum number of results (default 100, max 1000).
    #[serde(default = "default_limit")]
    #[schema(default = 100, minimum = 1, maximum = 1000)]
    pub limit: usize,
}

fn default_limit() -> usize {
    100
}

/// Response from listing sources.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ListSourcesResponse {
    /// The sources matching the query.
    pub sources: Vec<SourceRecordDto>,

    /// Number of sources returned.
    pub count: usize,
}

/// Compact source metadata for enriching skeptic/query responses.
///
/// This is a lightweight version of SourceRecordDto used when
/// enriching assertion responses with source metadata.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SourceMetadataDto {
    /// Human-readable label for the source.
    pub label: String,

    /// Authority tier (0-5).
    pub tier: u8,

    /// Human-readable tier label.
    pub tier_label: String,

    /// Optional URL where the source can be accessed.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    /// Current status (active, deprecated, quarantined).
    pub status: String,
}

impl From<SourceRecord> for SourceMetadataDto {
    fn from(record: SourceRecord) -> Self {
        let tier_label = record.tier_label().to_string();
        let status = record.status.as_str().to_string();
        Self { label: record.label, tier: record.tier, tier_label, url: record.url, status }
    }
}
