use anchor_lang::prelude::*;

declare_id!("E5e8JogsRqjXAEa3RKxFFVShRfD8TMjoAporRMvMLuYE");

#[program]
pub mod identity_registry {
    use super::*;

    pub fn initialize(ctx: Context<InitializeRegistry>) -> Result<()> {
        let deployment_authorized = false;
        require!(deployment_authorized, IdentityError::BootstrapDisabled);
        let registry = &mut ctx.accounts.registry_state;
        registry.authority = ctx.accounts.authority.key();
        registry.total_agents = 0;
        registry.created_at = Clock::get()?.unix_timestamp;
        Ok(())
    }

    pub fn register_agent(
        ctx: Context<RegisterAgent>,
        name: String,
        description: String,
    ) -> Result<()> {
        require!(name.len() <= 64, IdentityError::NameTooLong);
        require!(description.len() <= 256, IdentityError::DescriptionTooLong);

        let agent = &mut ctx.accounts.agent_identity;
        agent.agent_address = ctx.accounts.agent.key();
        agent.name = name.clone();
        agent.description = description;
        agent.registered_at = Clock::get()?.unix_timestamp;
        agent.authority = ctx.accounts.agent.key();
        agent.active = true;

        let registry = &mut ctx.accounts.registry_state;
        registry.total_agents = registry
            .total_agents
            .checked_add(1)
            .ok_or(IdentityError::ArithmeticOverflow)?;

        emit!(AgentRegistered {
            agent_address: agent.agent_address,
            name,
            registered_at: agent.registered_at,
        });
        Ok(())
    }

    pub fn update_agent(
        ctx: Context<UpdateAgent>,
        name: String,
        description: String,
    ) -> Result<()> {
        require!(name.len() <= 64, IdentityError::NameTooLong);
        require!(description.len() <= 256, IdentityError::DescriptionTooLong);

        let agent = &mut ctx.accounts.agent_identity;
        agent.name = name;
        agent.description = description;

        emit!(AgentUpdated {
            agent_address: agent.agent_address,
            updated_at: Clock::get()?.unix_timestamp,
        });
        Ok(())
    }

    pub fn deregister_agent(ctx: Context<DeregisterAgent>) -> Result<()> {
        let agent = &mut ctx.accounts.agent_identity;
        require!(agent.active, IdentityError::AgentNotActive);
        agent.active = false;

        let registry = &mut ctx.accounts.registry_state;
        registry.total_agents = registry
            .total_agents
            .checked_sub(1)
            .ok_or(IdentityError::AccountingInvariantViolation)?;

        emit!(AgentDeregistered {
            agent_address: agent.agent_address,
            deregistered_at: Clock::get()?.unix_timestamp,
        });
        Ok(())
    }
}

#[derive(Accounts)]
pub struct InitializeRegistry<'info> {
    #[account(
        init,
        payer = authority,
        space = 8 + 32 + 8 + 8,
        seeds = [b"registry"],
        bump
    )]
    pub registry_state: Account<'info, RegistryState>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct RegisterAgent<'info> {
    #[account(
        mut,
        seeds = [b"registry"],
        bump
    )]
    pub registry_state: Account<'info, RegistryState>,
    #[account(
        init,
        payer = agent,
        space = 8 + 32 + 4 + 64 + 4 + 256 + 8 + 32 + 1,
        seeds = [b"agent", agent.key().as_ref()],
        bump
    )]
    pub agent_identity: Account<'info, AgentIdentity>,
    #[account(mut)]
    pub agent: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct UpdateAgent<'info> {
    #[account(
        mut,
        seeds = [b"agent", authority.key().as_ref()],
        bump,
        has_one = authority
    )]
    pub agent_identity: Account<'info, AgentIdentity>,
    #[account(mut)]
    pub authority: Signer<'info>,
}

#[derive(Accounts)]
pub struct DeregisterAgent<'info> {
    #[account(
        mut,
        seeds = [b"registry"],
        bump
    )]
    pub registry_state: Account<'info, RegistryState>,
    #[account(
        mut,
        close = authority,
        seeds = [b"agent", authority.key().as_ref()],
        bump,
        has_one = authority
    )]
    pub agent_identity: Account<'info, AgentIdentity>,
    #[account(mut)]
    pub authority: Signer<'info>,
}

#[account]
pub struct RegistryState {
    pub authority: Pubkey,
    pub total_agents: u64,
    pub created_at: i64,
}

#[account]
pub struct AgentIdentity {
    pub agent_address: Pubkey,
    pub name: String,
    pub description: String,
    pub registered_at: i64,
    pub authority: Pubkey,
    pub active: bool,
}

#[event]
pub struct AgentRegistered {
    pub agent_address: Pubkey,
    pub name: String,
    pub registered_at: i64,
}

#[event]
pub struct AgentUpdated {
    pub agent_address: Pubkey,
    pub updated_at: i64,
}

#[event]
pub struct AgentDeregistered {
    pub agent_address: Pubkey,
    pub deregistered_at: i64,
}

#[error_code]
pub enum IdentityError {
    #[msg("Name exceeds maximum length of 64 characters")]
    NameTooLong,
    #[msg("Description exceeds maximum length of 256 characters")]
    DescriptionTooLong,
    #[msg("Arithmetic overflow")]
    ArithmeticOverflow,
    #[msg("Identity registry accounting invariant violated")]
    AccountingInvariantViolation,
    #[msg("Agent is already deregistered")]
    AgentNotActive,
    #[msg("Bootstrap is disabled until a source-backed partner authority design is approved")]
    BootstrapDisabled,
}
