use serde::{Serialize, Deserialize};
use url::Url;
use sha2::{Digest, Sha256};
use thiserror::Error;

pub mod envelope;
pub mod commands;
pub mod store;
pub mod temporal;
pub mod dht;
pub mod bzzz;
pub mod ucxl_codes;

pub use envelope::*;
pub use commands::*;
pub use store::*;
pub use dht::*;
pub use bzzz::*;
pub use ucxl_codes::*;
pub use temporal::{TemporalQuery, TemporalState, TemporalEngine, TimelineView, VersionDiff, ContentChange, ChangeType, ReconstructionInfo, VersionContext, TimelineEntry, ChangeSummary, BranchTimeline};

#[derive(Error, Debug)]
pub enum UCXLError {
    #[error("Invalid UCXL URI scheme: {0}")]
    InvalidScheme(String),
    #[error("Failed to parse URI: {0}")]
    ParseError(String),
    #[error("Envelope not found: {0}")]
    EnvelopeNotFound(String),
    #[error("Storage error: {0}")]
    StorageError(String),
    #[error("Serialization error: {0}")]
    SerializationError(String),
    #[error("Temporal error: {0}")]
    TemporalError(String),
}

pub type Result<T> = std::result::Result<T, UCXLError>;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UCXLUri {
    pub full_uri: String,
    pub scheme: String,
    pub authority: Option<String>,
    pub path: String,
    pub query: Option<String>,
    pub fragment: Option<String>,
}

impl UCXLUri {
    pub fn new(uri: &str) -> Result<Self> {
        let url = Url::parse(uri).map_err(|e| UCXLError::ParseError(e.to_string()))?;
        
        if url.scheme() != "ucxl" {
            return Err(UCXLError::InvalidScheme(url.scheme().to_string()));
        }
        
        Ok(UCXLUri {
            full_uri: uri.to_string(),
            scheme: url.scheme().to_string(),
            authority: url.host_str().map(|h| h.to_string()),
            path: url.path().to_string(),
            query: url.query().map(|q| q.to_string()),
            fragment: url.fragment().map(|f| f.to_string()),
        })
    }
    
    pub fn to_content_hash(&self) -> String {
        let mut hasher = Sha256::new();
        hasher.update(self.full_uri.as_bytes());
        hex::encode(hasher.finalize())
    }
}

pub fn parse_ucxl_uri(uri: &str) -> Result<UCXLUri> {
    UCXLUri::new(uri)
}

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

    #[test]
    fn test_parse_valid_ucxl_uri() {
        let uri = "ucxl://example.com/path?query=value#fragment";
        let result = UCXLUri::new(uri);
        assert!(result.is_ok());
        
        let ucxl_uri = result.unwrap();
        assert_eq!(ucxl_uri.full_uri, uri);
        assert_eq!(ucxl_uri.scheme, "ucxl");
        assert_eq!(ucxl_uri.authority, Some("example.com".to_string()));
        assert_eq!(ucxl_uri.path, "/path");
        assert_eq!(ucxl_uri.query, Some("query=value".to_string()));
        assert_eq!(ucxl_uri.fragment, Some("fragment".to_string()));
    }

    #[test]
    fn test_parse_invalid_scheme() {
        let uri = "http://some-id";
        let result = UCXLUri::new(uri);
        assert!(result.is_err());
        
        match result {
            Err(UCXLError::InvalidScheme(scheme)) => assert_eq!(scheme, "http"),
            _ => panic!("Expected InvalidScheme error"),
        }
    }
    
    #[test]
    fn test_content_hash() {
        let uri = "ucxl://example.com/test";
        let ucxl_uri = UCXLUri::new(uri).unwrap();
        let hash = ucxl_uri.to_content_hash();
        assert!(!hash.is_empty());
        assert_eq!(hash.len(), 64); // SHA256 produces 64 hex characters
    }
}