use crate::parser::Context;
use crate::proto::*;
use crate::{Entity, EntityEvents, GameEvent, StringTable};
use std::cell::RefCell;
use std::rc::Rc;

#[cfg(feature = "dota")]
use crate::event::CombatLogEntry;

/// Result type for observer callbacks.
///
/// This is a convenience alias for `anyhow::Result<()>`.
///
/// Methods generated by the `#[observer]` macro may also return
/// `Result<(), ParserError>` or another `Result` whose error can convert into
/// `anyhow::Error`. Concrete error types, including `ParserError`, are kept
/// inside the `anyhow::Error` and can be recovered with downcasting from
/// [`ParserError::ObserverError`](crate::error::ParserError::ObserverError).
///
/// # Examples
///
/// ```no_run
/// use source2_demo::prelude::*;
///
/// #[derive(Default)]
/// struct FailingObserver;
///
/// #[observer]
/// impl FailingObserver {
///     #[on_tick_start]
///     fn on_tick_start(&mut self) -> Result<(), ParserError> {
///         Err(ParserError::WrongMagic)
///     }
/// }
///
/// let mut parser = Parser::from_reader(std::fs::File::open("replay.dem")?)?;
/// parser.register_observer::<FailingObserver>();
///
/// match parser.run_to_end() {
///     Err(ParserError::ObserverError(error)) => {
///         if let Some(ParserError::WrongMagic) = error.downcast_ref::<ParserError>() {
///             println!("observer returned ParserError::WrongMagic");
///         }
///     }
///     Err(error) => {
///         println!("parser error: {error}");
///     }
///     Ok(()) => {}
/// }
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub type ObserverResult = anyhow::Result<()>;

bitflags::bitflags! {
    /// Bitflags for declaring observer interests.
    ///
    /// Use these flags in the [`Observer::interests`] method to specify which
    /// events your observer wants to receive. This allows the parser to skip
    /// unnecessary processing for events no observer cares about.
    ///
    /// # Examples
    ///
    /// ```
    /// use source2_demo::prelude::*;
    ///
    /// #[derive(Default)]
    /// struct EntityTracker;
    ///
    /// impl Observer for EntityTracker {
    ///     fn interests(&self) -> Interests {
    ///         // Track entities and receive tick events
    ///         Interests::ENTITY_STATE | Interests::ENTITY_EVENTS | Interests::TICK_START
    ///     }
    ///
    ///     fn on_entity(&mut self, ctx: &Context, event: EntityEvents, entity: &Entity) -> ObserverResult {
    ///         // Handle entity updates
    ///         Ok(())
    ///     }
    ///
    ///     fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
    ///         // Handle tick start
    ///         Ok(())
    ///     }
    /// }
    /// ```
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    pub struct Interests: u64 {
        /// Interest in outer demo messages (`EDemoCommands`).
        const DEMO_MESSAGE = 1 << 0;
        /// Interest in net messages (`NetMessages`).
        const NET_MESSAGE = 1 << 1;
        /// Interest in SVC messages (`SvcMessages`).
        const SVC_MESSAGE = 1 << 2;

        /// Interest in base user messages (`EBaseUserMessages`).
        const BASE_USER_MESSAGE = 1 << 3;
        /// Interest in base game events (`EBaseGameEvents`) and decoded game events.
        const BASE_GAME_EVENT = 1 << 4;

        /// Interest in tick start events.
        const TICK_START = 1 << 5;
        /// Interest in tick end events.
        const TICK_END = 1 << 6;

        /// Maintain entity state while parsing.
        const ENTITY_STATE = 1 << 7;
        /// Interest in entity create/update/delete events.
        const ENTITY_EVENTS = 1 << 8;
        /// Maintain string table state while parsing.
        const STRING_TABLE_STATE = 1 << 9;
        /// Interest in string table update events.
        const STRING_TABLE_ENTRIES = 1 << 10;

        /// Interest in the replay end event.
        const REPLAY_END = 1 << 11;

        #[cfg(feature = "dota")]
        /// Interest in Dota 2 user messages (`EDotaUserMessages`).
        const DOTA_USER_MESSAGE = 1 << 12;

        #[cfg(feature = "dota")]
        /// Interest in combat log entries (Dota 2 only).
        const COMBAT_LOG_ENTRIES = 1 << 13;


        #[cfg(feature = "deadlock")]
        /// Interest in Citadel/Deadlock user messages (`CitadelUserMessageIds`).
        const CITADEL_USER_MESSAGE = 1 << 14;

        #[cfg(feature = "deadlock")]
        /// Interest in Citadel/Deadlock game events (`ECitadelGameEvents`).
        const CITADEL_GAME_EVENT = 1 << 15;

        #[cfg(feature = "cs2")]
        /// Interest in CS2 user messages (`ECstrike15UserMessages`).
        const CS2_USER_MESSAGE = 1 << 16;

        #[cfg(feature = "cs2")]
        /// Interest in CS2 game events (`ECsgoGameEvents`).
        const CS2_GAME_EVENT = 1 << 17;
    }
}

/// Observer trait for handling demo file events.
///
/// Implement this trait to receive callbacks as the parser processes the demo
/// file. You can either implement it manually or use the `#[observer]`
/// attribute macro for a more convenient approach.
///
/// # Interest-based Filtering
///
/// The [`interests`](Observer::interests) method allows you to declare which
/// events your observer wants to receive. This is crucial for performance as it
/// allows the parser to skip processing events that no observer cares about.
///
/// # Examples
///
/// ## Using the `#[observer]` macro (recommended)
///
/// ```ignore
/// use source2_demo::prelude::*;
/// use source2_demo_protobufs::CDotaUserMsgChatMessage;
///
/// #[derive(Default)]
/// struct GameStats {
///     combat_logs: u32,
///     messages: u32,
/// }
///
/// #[observer]
/// impl GameStats {
///     #[on_message]
///     fn on_chat_msg(&mut self, ctx: &Context, msg: CDotaUserMsgChatMessage) -> ObserverResult {
///         self.messages += 1;
///         Ok(())
///     }
///
///     #[on_combat_log]
///     fn on_combat_log(&mut self, ctx: &Context, entry: &CombatLogEntry) -> ObserverResult {
///         self.combat_logs += 1;
///         Ok(())
///     }
/// }
/// ```
///
/// ## Manual implementation
///
/// ```no_run
/// use source2_demo::prelude::*;
///
/// #[derive(Default)]
/// struct EntityCounter {
///     count: usize,
/// }
///
/// impl Observer for EntityCounter {
///     fn interests(&self) -> Interests {
///         Interests::ENTITY_STATE | Interests::ENTITY_EVENTS
///     }
///
///     fn on_entity(
///         &mut self,
///         ctx: &Context,
///         event: EntityEvents,
///         entity: &Entity,
///     ) -> ObserverResult {
///         if event == EntityEvents::Created {
///             self.count += 1;
///         }
///         Ok(())
///     }
/// }
/// ```
///
/// ## Combining multiple interests
///
/// ```no_run
/// use source2_demo::prelude::*;
///
/// #[derive(Default)]
/// struct MultiObserver;
///
/// impl Observer for MultiObserver {
///     fn interests(&self) -> Interests {
///         Interests::TICK_START
///             | Interests::TICK_END
///             | Interests::ENTITY_STATE
///             | Interests::ENTITY_EVENTS
///     }
///
///     fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
///         println!("Tick {}", ctx.tick());
///         Ok(())
///     }
///
///     fn on_entity(
///         &mut self,
///         ctx: &Context,
///         event: EntityEvents,
///         entity: &Entity,
///     ) -> ObserverResult {
///         // Process entities
///         Ok(())
///     }
/// }
/// ```
#[allow(unused_variables)]
pub trait Observer {
    /// Declares which events this observer is interested in.
    ///
    /// Return an empty [`Interests`] to receive no events, or combine flags
    /// using the `|` operator. This method is called once when the observer
    /// is registered.
    ///
    /// # Default
    ///
    /// Returns [`Interests::empty()`] by default (no events).
    fn interests(&self) -> Interests {
        Interests::empty()
    }

    /// Called when a demo command is received.
    ///
    /// Requires [`Interests::DEMO_MESSAGE`] to be set.
    #[cold]
    #[inline(never)]
    fn on_demo_command(
        &mut self,
        ctx: &Context,
        msg_type: EDemoCommands,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when a net message is received.
    ///
    /// Requires [`Interests::NET_MESSAGE`] to be set.
    #[cold]
    #[inline(never)]
    fn on_net_message(
        &mut self,
        ctx: &Context,
        msg_type: NetMessages,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when an SVC (server-to-client) message is received.
    ///
    /// Requires [`Interests::SVC_MESSAGE`] to be set.
    #[cold]
    #[inline(never)]
    fn on_svc_message(
        &mut self,
        ctx: &Context,
        msg_type: SvcMessages,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when a base user message is received.
    ///
    /// Requires [`Interests::BASE_USER_MESSAGE`] to be set.
    #[cold]
    #[inline(never)]
    fn on_base_user_message(
        &mut self,
        ctx: &Context,
        msg_type: EBaseUserMessages,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when a base game event is received.
    ///
    /// Requires [`Interests::BASE_GAME_EVENT`] to be set.
    #[cold]
    #[inline(never)]
    fn on_base_game_event(
        &mut self,
        ctx: &Context,
        msg_type: EBaseGameEvents,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called at the start of each tick.
    ///
    /// Requires [`Interests::TICK_START`] to be set.
    #[cold]
    #[inline(never)]
    fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
        Ok(())
    }

    /// Called at the end of each tick.
    ///
    /// Requires [`Interests::TICK_END`] to be set.
    #[cold]
    #[inline(never)]
    fn on_tick_end(&mut self, ctx: &Context) -> ObserverResult {
        Ok(())
    }

    /// Called when an entity is created, updated, or deleted.
    ///
    /// Requires [`Interests::ENTITY_EVENTS`] and [`Interests::ENTITY_STATE`] to
    /// be set.
    #[cold]
    #[inline(never)]
    fn on_entity(&mut self, ctx: &Context, event: EntityEvents, entity: &Entity) -> ObserverResult {
        Ok(())
    }

    /// Called when a game event occurs.
    ///
    /// Requires [`Interests::BASE_GAME_EVENT`] to be set.
    #[cold]
    #[inline(never)]
    fn on_game_event(&mut self, ctx: &Context, ge: &GameEvent) -> ObserverResult {
        Ok(())
    }

    /// Called when a string table is updated.
    ///
    /// Requires [`Interests::STRING_TABLE_ENTRIES`] and
    /// [`Interests::STRING_TABLE_STATE`] to be set.
    #[cold]
    #[inline(never)]
    fn on_string_table(
        &mut self,
        ctx: &Context,
        st: &StringTable,
        modified: &[i32],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when the replay ends.
    ///
    /// Requires [`Interests::REPLAY_END`] to be set.
    /// This is the last callback before parsing completes.
    ///
    /// # Arguments
    ///
    /// * `ctx` - Current replay context
    #[cold]
    #[inline(never)]
    fn on_stop(&mut self, ctx: &Context) -> ObserverResult {
        Ok(())
    }

    /// Called when a combat log entry is received (Dota 2 only).
    ///
    /// Combat log entries describe in-game events like damage, healing, kills,
    /// etc. Only available with the `dota` feature enabled.
    ///
    /// Requires [`Interests::COMBAT_LOG_ENTRIES`] to be set.
    #[cold]
    #[inline(never)]
    #[cfg(feature = "dota")]
    fn on_combat_log(&mut self, ctx: &Context, cle: &CombatLogEntry) -> ObserverResult {
        Ok(())
    }

    /// Called when a Dota 2 user message is received.
    ///
    /// Dota 2 specific user messages. Only available with the `dota` feature
    /// enabled.
    ///
    /// Requires [`Interests::DOTA_USER_MESSAGE`] to be set.
    #[cold]
    #[inline(never)]
    #[cfg(feature = "dota")]
    fn on_dota_user_message(
        &mut self,
        ctx: &Context,
        msg_type: EDotaUserMessages,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when a Citadel/Deadlock game event is received.
    ///
    /// Deadlock specific game events. Only available with the `deadlock`
    /// feature enabled.
    ///
    /// Requires [`Interests::CITADEL_GAME_EVENT`] to be set.
    #[cold]
    #[inline(never)]
    #[cfg(feature = "deadlock")]
    fn on_citadel_game_event(
        &mut self,
        ctx: &Context,
        msg_type: ECitadelGameEvents,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when a Citadel/Deadlock user message is received.
    ///
    /// Deadlock specific user messages. Only available with the `deadlock`
    /// feature enabled.
    ///
    /// Requires [`Interests::CITADEL_USER_MESSAGE`] to be set.
    #[cold]
    #[inline(never)]
    #[cfg(feature = "deadlock")]
    fn on_citadel_user_message(
        &mut self,
        ctx: &Context,
        msg_type: CitadelUserMessageIds,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when a Counter-Strike 2 user message is received.
    ///
    /// CS2 specific user messages. Only available with the `cs2` feature
    /// enabled.
    ///
    /// Requires [`Interests::CS2_USER_MESSAGE`] to be set.
    #[cold]
    #[inline(never)]
    #[cfg(feature = "cs2")]
    fn on_cs2_user_message(
        &mut self,
        ctx: &Context,
        msg_type: ECstrike15UserMessages,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }

    /// Called when a Counter-Strike 2 game event is received.
    ///
    /// CS2 specific game events. Only available with the `cs2` feature enabled.
    ///
    /// Requires [`Interests::CS2_GAME_EVENT`] to be set.
    #[cold]
    #[inline(never)]
    #[cfg(feature = "cs2")]
    fn on_cs2_game_event(
        &mut self,
        ctx: &Context,
        msg_type: ECsgoGameEvents,
        msg: &[u8],
    ) -> ObserverResult {
        Ok(())
    }
}

impl<T> Observer for Rc<RefCell<T>>
where
    T: Observer,
{
    fn interests(&self) -> Interests {
        self.borrow().interests()
    }

    fn on_demo_command(
        &mut self,
        ctx: &Context,
        msg_type: EDemoCommands,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_demo_command(ctx, msg_type, msg)
    }

    fn on_net_message(
        &mut self,
        ctx: &Context,
        msg_type: NetMessages,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_net_message(ctx, msg_type, msg)
    }

    fn on_svc_message(
        &mut self,
        ctx: &Context,
        msg_type: SvcMessages,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_svc_message(ctx, msg_type, msg)
    }

    fn on_base_user_message(
        &mut self,
        ctx: &Context,
        msg_type: EBaseUserMessages,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_base_user_message(ctx, msg_type, msg)
    }

    fn on_base_game_event(
        &mut self,
        ctx: &Context,
        msg_type: EBaseGameEvents,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_base_game_event(ctx, msg_type, msg)
    }

    fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
        self.borrow_mut().on_tick_start(ctx)
    }

    fn on_tick_end(&mut self, ctx: &Context) -> ObserverResult {
        self.borrow_mut().on_tick_end(ctx)
    }

    fn on_entity(&mut self, ctx: &Context, event: EntityEvents, entity: &Entity) -> ObserverResult {
        self.borrow_mut().on_entity(ctx, event, entity)
    }

    fn on_game_event(&mut self, ctx: &Context, ge: &GameEvent) -> ObserverResult {
        self.borrow_mut().on_game_event(ctx, ge)
    }

    fn on_string_table(
        &mut self,
        ctx: &Context,
        st: &StringTable,
        modified: &[i32],
    ) -> ObserverResult {
        self.borrow_mut().on_string_table(ctx, st, modified)
    }

    fn on_stop(&mut self, ctx: &Context) -> ObserverResult {
        self.borrow_mut().on_stop(ctx)
    }

    #[cfg(feature = "dota")]
    fn on_combat_log(&mut self, ctx: &Context, cle: &CombatLogEntry) -> ObserverResult {
        self.borrow_mut().on_combat_log(ctx, cle)
    }

    #[cfg(feature = "dota")]
    fn on_dota_user_message(
        &mut self,
        ctx: &Context,
        msg_type: EDotaUserMessages,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_dota_user_message(ctx, msg_type, msg)
    }

    #[cfg(feature = "deadlock")]
    fn on_citadel_game_event(
        &mut self,
        ctx: &Context,
        msg_type: ECitadelGameEvents,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_citadel_game_event(ctx, msg_type, msg)
    }

    #[cfg(feature = "deadlock")]
    fn on_citadel_user_message(
        &mut self,
        ctx: &Context,
        msg_type: CitadelUserMessageIds,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut()
            .on_citadel_user_message(ctx, msg_type, msg)
    }

    #[cfg(feature = "cs2")]
    fn on_cs2_user_message(
        &mut self,
        ctx: &Context,
        msg_type: ECstrike15UserMessages,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_cs2_user_message(ctx, msg_type, msg)
    }

    #[cfg(feature = "cs2")]
    fn on_cs2_game_event(
        &mut self,
        ctx: &Context,
        msg_type: ECsgoGameEvents,
        msg: &[u8],
    ) -> ObserverResult {
        self.borrow_mut().on_cs2_game_event(ctx, msg_type, msg)
    }
}
