// Minimal Solana HDGL Program
// Stores 8 dimensions as u64 values (analog * 10^9)
// Simple, elegant, no unnecessary complexity

use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program_error::ProgramError,
    pubkey::Pubkey,
};
use borsh::{BorshDeserialize, BorshSerialize};

// φ constant scaled to u64 (φ * 10^9)
const PHI: u64 = 1_618_033_988;
const PHI_SQ: u64 = 2_618_033_988;
const INV_PHI: u64 = 618_033_988;
const SCALE: u64 = 1_000_000_000;

// HDGL State: Just 8 dimensions (64 bytes) + metadata (32 bytes) = 96 bytes total
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct HDGLState {
    // The 8 analog dimensions (D1-D8)
    pub d1: u64,  // Strand A, Wave +
    pub d2: u64,  // Strand A, Wave 0
    pub d3: u64,  // Strand A, Wave -
    pub d4: u64,  // Strand B, Wave +
    pub d5: u64,  // Strand B, Wave 0
    pub d6: u64,  // Strand B, Wave -
    pub d7: u64,  // Strand A+B superposition
    pub d8: u64,  // Full hybrid lattice
    
    // Metadata
    pub evolution_count: u64,
    pub last_commitment: [u8; 32],
}

impl Default for HDGLState {
    fn default() -> Self {
        Self {
            d1: 1 * SCALE,  // D1 = 1.0
            d2: 2 * SCALE,  // D2 = 2.0
            d3: 3 * SCALE,  // D3 = 3.0
            d4: 4 * SCALE,  // D4 = 4.0
            d5: 5 * SCALE,  // D5 = 5.0
            d6: 6 * SCALE,  // D6 = 6.0
            d7: 7 * SCALE,  // D7 = 7.0
            d8: 8 * SCALE,  // D8 = 8.0
            evolution_count: 0,
            last_commitment: [0u8; 32],
        }
    }
}

impl HDGLState {
    // Get dimension by index (0-7 for D1-D8)
    pub fn get_dim(&self, idx: usize) -> u64 {
        match idx {
            0 => self.d1,
            1 => self.d2,
            2 => self.d3,
            3 => self.d4,
            4 => self.d5,
            5 => self.d6,
            6 => self.d7,
            7 => self.d8,
            _ => 0,
        }
    }
    
    // Set dimension by index
    pub fn set_dim(&mut self, idx: usize, value: u64) {
        match idx {
            0 => self.d1 = value,
            1 => self.d2 = value,
            2 => self.d3 = value,
            3 => self.d4 = value,
            4 => self.d5 = value,
            5 => self.d6 = value,
            6 => self.d7 = value,
            7 => self.d8 = value,
            _ => {},
        }
    }
    
    // φ-evolution: dim → dim * φ
    pub fn phi_evolve(&mut self, dim_idx: usize) {
        let current = self.get_dim(dim_idx);
        let evolved = current.saturating_mul(PHI) / SCALE;
        self.set_dim(dim_idx, evolved);
    }
    
    // φ-contraction: dim → dim / φ
    pub fn phi_contract(&mut self, dim_idx: usize) {
        let current = self.get_dim(dim_idx);
        let contracted = current.saturating_mul(INV_PHI) / SCALE;
        self.set_dim(dim_idx, contracted);
    }
    
    // φ-addition: a ⊕ b = (a + b) * φ²
    pub fn phi_add(&mut self, dim_a: usize, dim_b: usize, dim_result: usize) {
        let a = self.get_dim(dim_a);
        let b = self.get_dim(dim_b);
        let sum = a.saturating_add(b);
        let result = sum.saturating_mul(PHI_SQ) / SCALE;
        self.set_dim(dim_result, result);
    }
    
    // Strand superposition: (a*φ + b/φ) / φ²
    pub fn superposition(&mut self, dim_a: usize, dim_b: usize, dim_result: usize) {
        let a = self.get_dim(dim_a);
        let b = self.get_dim(dim_b);
        
        let a_phi = a.saturating_mul(PHI) / SCALE;
        let b_inv_phi = b.saturating_mul(INV_PHI) / SCALE;
        let sum = a_phi.saturating_add(b_inv_phi);
        let result = sum.saturating_mul(SCALE) / PHI_SQ;
        
        self.set_dim(dim_result, result);
    }
    
    // Generate commitment hash (Keccak256 of all dimensions)
    pub fn generate_commitment(&mut self) {
        use solana_program::keccak;
        
        let mut data = Vec::with_capacity(64);
        for i in 0..8 {
            data.extend_from_slice(&self.get_dim(i).to_le_bytes());
        }
        
        let hash = keccak::hash(&data);
        self.last_commitment.copy_from_slice(&hash.to_bytes());
        self.evolution_count += 1;
    }
}

// Instructions
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum HDGLInstruction {
    // Initialize state with default dimensions
    Initialize,
    
    // φ-evolve dimension: PhiEvolve { dim_idx: u8 }
    PhiEvolve { dim_idx: u8 },
    
    // φ-contract dimension: PhiContract { dim_idx: u8 }
    PhiContract { dim_idx: u8 },
    
    // φ-add two dimensions: PhiAdd { dim_a: u8, dim_b: u8, dim_result: u8 }
    PhiAdd { dim_a: u8, dim_b: u8, dim_result: u8 },
    
    // Create superposition: Superposition { dim_a: u8, dim_b: u8, dim_result: u8 }
    Superposition { dim_a: u8, dim_b: u8, dim_result: u8 },
    
    // Evolve all dimensions by φ
    EvolveAll,
    
    // Generate commitment hash
    GenerateCommitment,
}

entrypoint!(process_instruction);

pub fn process_instruction(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = HDGLInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;
    
    let account_info_iter = &mut accounts.iter();
    let state_account = next_account_info(account_info_iter)?;
    
    // Ensure account is writable
    if !state_account.is_writable {
        return Err(ProgramError::InvalidAccountData);
    }
    
    match instruction {
        HDGLInstruction::Initialize => {
            // Initialize with default state
            let state = HDGLState::default();
            state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
            msg!("HDGL state initialized");
            Ok(())
        }
        
        HDGLInstruction::PhiEvolve { dim_idx } => {
            if dim_idx > 7 {
                return Err(ProgramError::InvalidArgument);
            }
            
            let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
                .map_err(|_| ProgramError::InvalidAccountData)?;
            
            state.phi_evolve(dim_idx as usize);
            state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
            
            msg!("Evolved D{} by φ", dim_idx + 1);
            Ok(())
        }
        
        HDGLInstruction::PhiContract { dim_idx } => {
            if dim_idx > 7 {
                return Err(ProgramError::InvalidArgument);
            }
            
            let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
                .map_err(|_| ProgramError::InvalidAccountData)?;
            
            state.phi_contract(dim_idx as usize);
            state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
            
            msg!("Contracted D{} by 1/φ", dim_idx + 1);
            Ok(())
        }
        
        HDGLInstruction::PhiAdd { dim_a, dim_b, dim_result } => {
            if dim_a > 7 || dim_b > 7 || dim_result > 7 {
                return Err(ProgramError::InvalidArgument);
            }
            
            let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
                .map_err(|_| ProgramError::InvalidAccountData)?;
            
            state.phi_add(dim_a as usize, dim_b as usize, dim_result as usize);
            state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
            
            msg!("φ-added D{} + D{} → D{}", dim_a + 1, dim_b + 1, dim_result + 1);
            Ok(())
        }
        
        HDGLInstruction::Superposition { dim_a, dim_b, dim_result } => {
            if dim_a > 7 || dim_b > 7 || dim_result > 7 {
                return Err(ProgramError::InvalidArgument);
            }
            
            let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
                .map_err(|_| ProgramError::InvalidAccountData)?;
            
            state.superposition(dim_a as usize, dim_b as usize, dim_result as usize);
            state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
            
            msg!("Superposition D{} × D{} → D{}", dim_a + 1, dim_b + 1, dim_result + 1);
            Ok(())
        }
        
        HDGLInstruction::EvolveAll => {
            let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
                .map_err(|_| ProgramError::InvalidAccountData)?;
            
            // Evolve all 8 dimensions by φ
            for i in 0..8 {
                state.phi_evolve(i);
            }
            
            state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
            
            msg!("Evolved all dimensions by φ");
            Ok(())
        }
        
        HDGLInstruction::GenerateCommitment => {
            let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
                .map_err(|_| ProgramError::InvalidAccountData)?;
            
            state.generate_commitment();
            state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
            
            msg!("Generated commitment, evolution #{}", state.evolution_count);
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_phi_evolution() {
        let mut state = HDGLState::default();
        let initial_d1 = state.d1;
        
        state.phi_evolve(0);
        
        // D1 should be multiplied by φ (≈ 1.618)
        assert!(state.d1 > initial_d1);
        assert!(state.d1 < initial_d1 * 2);
    }
    
    #[test]
    fn test_superposition() {
        let mut state = HDGLState::default();
        
        state.superposition(0, 3, 6);  // D1 × D4 → D7
        
        // D7 should be a φ-weighted combination
        assert!(state.d7 > 0);
    }
    
    #[test]
    fn test_commitment() {
        let mut state = HDGLState::default();
        
        state.generate_commitment();
        
        // Commitment should be non-zero
        assert_ne!(state.last_commitment, [0u8; 32]);
        assert_eq!(state.evolution_count, 1);
    }
}
