use std::fmt;
use std::str::FromStr;
use uuid::Uuid;
use serde::{Serialize, de::DeserializeOwned};

/// Trait for messages that know their own NATS subject.
pub trait SubjectMessage: Serialize + DeserializeOwned + Send + Sync {
    fn subject(&self) -> NatsSubject;
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NatsSubject {
    // --- Workflow Generic ---
    WorkflowCommand(String), // Dynamic routing key: workflow.cmd.{routing_key}
    WorkflowEventTaskCompleted, // workflow.evt.task_completed
    WorkflowCommandWildcard, // workflow.cmd.>

    // --- Commands ---
    WorkflowCommandStart,
    WorkflowCommandSyncState,
    DataFetchCommands,
    AnalysisCommandGenerateReport,

    // --- Events ---
    // Data Events
    DataFinancialsPersisted,
    DataFetchFailed,

    // Analysis Events
    AnalysisReportGenerated,
    AnalysisReportFailed,
    
    // Workflow Events (Dynamic)
    WorkflowProgress(Uuid),

    // --- Wildcards (For Subscription) ---
    AnalysisEventsWildcard,
    WorkflowCommandsWildcard,
    DataEventsWildcard,
}

impl fmt::Display for NatsSubject {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::WorkflowCommand(key) => write!(f, "workflow.cmd.{}", key),
            Self::WorkflowEventTaskCompleted => write!(f, "workflow.evt.task_completed"),
            Self::WorkflowCommandWildcard => write!(f, "workflow.cmd.>"),
            Self::WorkflowCommandStart => write!(f, "workflow.commands.start"),
            Self::WorkflowCommandSyncState => write!(f, "workflow.commands.sync_state"),
            Self::DataFetchCommands => write!(f, "data_fetch_commands"),
            Self::AnalysisCommandGenerateReport => write!(f, "analysis.commands.generate_report"),
            Self::DataFinancialsPersisted => write!(f, "events.data.financials_persisted"),
            Self::DataFetchFailed => write!(f, "events.data.fetch_failed"),
            Self::AnalysisReportGenerated => write!(f, "events.analysis.report_generated"),
            Self::AnalysisReportFailed => write!(f, "events.analysis.report_failed"),
            Self::WorkflowProgress(id) => write!(f, "events.workflow.{}", id),
            Self::AnalysisEventsWildcard => write!(f, "events.analysis.>"),
            Self::WorkflowCommandsWildcard => write!(f, "workflow.commands.>"),
            Self::DataEventsWildcard => write!(f, "events.data.>"),
        }
    }
}

impl FromStr for NatsSubject {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "workflow.evt.task_completed" => Ok(Self::WorkflowEventTaskCompleted),
            "workflow.cmd.>" => Ok(Self::WorkflowCommandWildcard),
            "workflow.commands.start" => Ok(Self::WorkflowCommandStart),
            "workflow.commands.sync_state" => Ok(Self::WorkflowCommandSyncState),
            "data_fetch_commands" => Ok(Self::DataFetchCommands),
            "analysis.commands.generate_report" => Ok(Self::AnalysisCommandGenerateReport),
            "events.data.financials_persisted" => Ok(Self::DataFinancialsPersisted),
            "events.data.fetch_failed" => Ok(Self::DataFetchFailed),
            "events.analysis.report_generated" => Ok(Self::AnalysisReportGenerated),
            "events.analysis.report_failed" => Ok(Self::AnalysisReportFailed),
            "events.analysis.>" => Ok(Self::AnalysisEventsWildcard),
            "workflow.commands.>" => Ok(Self::WorkflowCommandsWildcard),
            "events.data.>" => Ok(Self::DataEventsWildcard),
            _ => {
                if s.starts_with("events.workflow.") {
                    let uuid_str = s.trim_start_matches("events.workflow.");
                    if let Ok(uuid) = Uuid::parse_str(uuid_str) {
                        return Ok(Self::WorkflowProgress(uuid));
                    }
                }
                if s.starts_with("workflow.cmd.") {
                    let key = s.trim_start_matches("workflow.cmd.");
                    return Ok(Self::WorkflowCommand(key.to_string()));
                }
                Err(format!("Unknown or invalid subject: {}", s))
            }
        }
    }
}

// Implement TryFrom for convenience
impl TryFrom<&str> for NatsSubject {
    type Error = String;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        Self::from_str(value)
    }
}

impl TryFrom<String> for NatsSubject {
    type Error = String;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        Self::from_str(&value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_subject_round_trip() {
        let subjects = vec![
            (NatsSubject::WorkflowCommandStart, "workflow.commands.start"),
            (NatsSubject::WorkflowCommandSyncState, "workflow.commands.sync_state"),
            (NatsSubject::DataFetchCommands, "data_fetch_commands"),
            (NatsSubject::AnalysisCommandGenerateReport, "analysis.commands.generate_report"),
            (NatsSubject::DataFinancialsPersisted, "events.data.financials_persisted"),
            (NatsSubject::DataFetchFailed, "events.data.fetch_failed"),
            (NatsSubject::AnalysisReportGenerated, "events.analysis.report_generated"),
            (NatsSubject::AnalysisReportFailed, "events.analysis.report_failed"),
            (NatsSubject::AnalysisEventsWildcard, "events.analysis.>"),
        ];

        for (subject, string_val) in subjects {
            assert_eq!(subject.to_string(), string_val);
            assert_eq!(NatsSubject::from_str(string_val).unwrap(), subject);
        }
    }

    #[test]
    fn test_dynamic_subject() {
        let id = Uuid::new_v4();
        let subject = NatsSubject::WorkflowProgress(id);
        let expected = format!("events.workflow.{}", id);
        
        assert_eq!(subject.to_string(), expected);
        assert_eq!(NatsSubject::from_str(&expected).unwrap(), subject);
    }

    #[test]
    fn test_invalid_subject() {
        assert!(NatsSubject::from_str("invalid.subject").is_err());
        assert!(NatsSubject::from_str("events.workflow.invalid-uuid").is_err());
    }
}

