//! Core types for the Synor blockchain.
//!
//! This crate provides the fundamental data structures used throughout
//! the Synor blockchain, including blocks, transactions, addresses, and hashes.

#![allow(dead_code)]

pub mod address;
pub mod block;
pub mod hash;
pub mod transaction;

pub use address::{Address, AddressError};
pub use block::{Block, BlockHeader, BlockId};
pub use hash::{Hash256, HashError};
pub use transaction::{Transaction, TransactionId, TxInput, TxOutput};

/// Network identifier for Synor chains.
#[derive(
    Debug,
    Clone,
    Copy,
    Default,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    serde::Serialize,
    serde::Deserialize,
)]
pub enum Network {
    /// Main production network
    #[default]
    Mainnet,
    /// Public test network
    Testnet,
    /// Local development network
    Devnet,
}

impl Network {
    /// Returns the human-readable part (HRP) for Bech32 addresses.
    pub fn hrp(&self) -> &'static str {
        match self {
            Network::Mainnet => "synor",
            Network::Testnet => "tsynor",
            Network::Devnet => "dsynor",
        }
    }

    /// Returns the network magic bytes for P2P protocol.
    pub fn magic(&self) -> [u8; 4] {
        match self {
            Network::Mainnet => [0x53, 0x59, 0x4E, 0x52], // "SYNR"
            Network::Testnet => [0x54, 0x53, 0x59, 0x4E], // "TSYN"
            Network::Devnet => [0x44, 0x53, 0x59, 0x4E],  // "DSYN"
        }
    }
}

/// Token amount in the smallest unit (1 SYNOR = 10^8 sompi).
/// Similar to Bitcoin's satoshi.
#[derive(
    Debug,
    Clone,
    Copy,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Default,
    serde::Serialize,
    serde::Deserialize,
    borsh::BorshSerialize,
    borsh::BorshDeserialize,
)]
pub struct Amount(u64);

impl Amount {
    /// One SYNOR in sompi (smallest unit).
    pub const ONE_SYNOR: u64 = 100_000_000;

    /// Maximum possible amount (70 million SYNOR).
    pub const MAX_SUPPLY: u64 = 70_000_000 * Self::ONE_SYNOR;

    /// Zero amount.
    pub const ZERO: Amount = Amount(0);

    /// Creates a new amount from sompi.
    pub const fn from_sompi(sompi: u64) -> Self {
        Amount(sompi)
    }

    /// Creates a new amount from SYNOR.
    pub const fn from_synor(synor: u64) -> Self {
        Amount(synor * Self::ONE_SYNOR)
    }

    /// Returns the amount in sompi.
    pub const fn as_sompi(&self) -> u64 {
        self.0
    }

    /// Returns the amount in SYNOR (may lose precision).
    pub const fn as_synor(&self) -> u64 {
        self.0 / Self::ONE_SYNOR
    }

    /// Checked addition.
    pub fn checked_add(self, other: Amount) -> Option<Amount> {
        self.0.checked_add(other.0).map(Amount)
    }

    /// Checked subtraction.
    pub fn checked_sub(self, other: Amount) -> Option<Amount> {
        self.0.checked_sub(other.0).map(Amount)
    }

    /// Checked multiplication.
    pub fn checked_mul(self, multiplier: u64) -> Option<Amount> {
        self.0.checked_mul(multiplier).map(Amount)
    }

    /// Saturating addition.
    pub fn saturating_add(self, other: Amount) -> Amount {
        Amount(self.0.saturating_add(other.0))
    }

    /// Saturating subtraction.
    pub fn saturating_sub(self, other: Amount) -> Amount {
        Amount(self.0.saturating_sub(other.0))
    }
}

impl std::fmt::Display for Amount {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let synor = self.0 / Self::ONE_SYNOR;
        let sompi = self.0 % Self::ONE_SYNOR;
        if sompi == 0 {
            write!(f, "{} SYNOR", synor)
        } else {
            write!(f, "{}.{:08} SYNOR", synor, sompi)
        }
    }
}

impl From<u64> for Amount {
    fn from(sompi: u64) -> Self {
        Amount(sompi)
    }
}

impl From<Amount> for u64 {
    fn from(amount: Amount) -> Self {
        amount.0
    }
}

/// Timestamp in milliseconds since Unix epoch.
#[derive(
    Debug,
    Clone,
    Copy,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    serde::Serialize,
    serde::Deserialize,
    borsh::BorshSerialize,
    borsh::BorshDeserialize,
)]
pub struct Timestamp(u64);

impl Timestamp {
    /// Creates a new timestamp from milliseconds since Unix epoch.
    pub const fn from_millis(millis: u64) -> Self {
        Timestamp(millis)
    }

    /// Returns the current timestamp.
    pub fn now() -> Self {
        use std::time::{SystemTime, UNIX_EPOCH};
        let duration = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("Time went backwards");
        Timestamp(duration.as_millis() as u64)
    }

    /// Returns the timestamp in milliseconds.
    pub const fn as_millis(&self) -> u64 {
        self.0
    }

    /// Returns the timestamp in seconds.
    pub const fn as_secs(&self) -> u64 {
        self.0 / 1000
    }
}

impl Default for Timestamp {
    fn default() -> Self {
        Timestamp::now()
    }
}

/// Blue score - a key concept in GHOSTDAG for ordering blocks.
/// Represents the cumulative "blue work" up to and including a block.
#[derive(
    Debug,
    Clone,
    Copy,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Default,
    serde::Serialize,
    serde::Deserialize,
    borsh::BorshSerialize,
    borsh::BorshDeserialize,
)]
pub struct BlueScore(u64);

impl BlueScore {
    /// Creates a new blue score.
    pub const fn new(score: u64) -> Self {
        BlueScore(score)
    }

    /// Returns the blue score value.
    pub const fn value(&self) -> u64 {
        self.0
    }

    /// Increments the blue score by one.
    pub fn increment(&mut self) {
        self.0 += 1;
    }
}

impl From<u64> for BlueScore {
    fn from(score: u64) -> Self {
        BlueScore(score)
    }
}

impl From<BlueScore> for u64 {
    fn from(score: BlueScore) -> Self {
        score.0
    }
}

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

    #[test]
    fn test_amount_display() {
        assert_eq!(Amount::from_synor(100).to_string(), "100 SYNOR");
        assert_eq!(
            Amount::from_sompi(100_000_001).to_string(),
            "1.00000001 SYNOR"
        );
    }

    #[test]
    fn test_amount_arithmetic() {
        let a = Amount::from_synor(10);
        let b = Amount::from_synor(5);

        assert_eq!(a.checked_add(b), Some(Amount::from_synor(15)));
        assert_eq!(a.checked_sub(b), Some(Amount::from_synor(5)));
        assert_eq!(b.checked_sub(a), None);
    }

    #[test]
    fn test_network_hrp() {
        assert_eq!(Network::Mainnet.hrp(), "synor");
        assert_eq!(Network::Testnet.hrp(), "tsynor");
        assert_eq!(Network::Devnet.hrp(), "dsynor");
    }
}
