use chrono::{DateTime, Utc};
use serde::{Deserialize, Deserializer, Serialize};

use super::issue::TrackerType;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ScanTrigger {
    Scheduled,
    Webhook,
    Manual,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackedRepository {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<bson::oid::ObjectId>,
    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub git_url: String,
    #[serde(default = "default_branch")]
    pub default_branch: String,
    pub local_path: Option<String>,
    pub scan_schedule: Option<String>,
    #[serde(default)]
    pub webhook_enabled: bool,
    /// Auto-generated HMAC secret for verifying incoming webhooks
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub webhook_secret: Option<String>,
    pub tracker_type: Option<TrackerType>,
    pub tracker_owner: Option<String>,
    pub tracker_repo: Option<String>,
    /// Optional per-repo PAT for the issue tracker (GitHub/GitLab/Jira)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tracker_token: Option<String>,
    /// Optional auth token for HTTPS private repos (PAT or password)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub auth_token: Option<String>,
    /// Optional username for HTTPS auth (defaults to "x-access-token" for PATs)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub auth_username: Option<String>,
    pub last_scanned_commit: Option<String>,
    #[serde(default, deserialize_with = "deserialize_findings_count")]
    pub findings_count: u32,
    #[serde(
        default = "chrono::Utc::now",
        with = "super::serde_helpers::bson_datetime"
    )]
    pub created_at: DateTime<Utc>,
    #[serde(
        default = "chrono::Utc::now",
        with = "super::serde_helpers::bson_datetime"
    )]
    pub updated_at: DateTime<Utc>,
}

fn default_branch() -> String {
    "main".to_string()
}

fn deserialize_findings_count<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
    D: Deserializer<'de>,
{
    let bson = bson::Bson::deserialize(deserializer)?;
    match &bson {
        bson::Bson::Int32(n) => Ok(*n as u32),
        bson::Bson::Int64(n) => Ok(*n as u32),
        bson::Bson::Double(n) => Ok(*n as u32),
        _ => Ok(0),
    }
}

impl TrackedRepository {
    pub fn new(name: String, git_url: String) -> Self {
        let now = Utc::now();
        // Generate a random webhook secret (hex-encoded UUID v4, no dashes)
        let webhook_secret = uuid::Uuid::new_v4().to_string().replace('-', "");
        Self {
            id: None,
            name,
            git_url,
            default_branch: "main".to_string(),
            local_path: None,
            scan_schedule: None,
            auth_token: None,
            auth_username: None,
            webhook_enabled: false,
            webhook_secret: Some(webhook_secret),
            tracker_type: None,
            tracker_owner: None,
            tracker_repo: None,
            tracker_token: None,
            last_scanned_commit: None,
            findings_count: 0,
            created_at: now,
            updated_at: now,
        }
    }
}
