use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program_error::ProgramError,
    pubkey::Pubkey,
    rent::Rent,
    system_instruction,
    sysvar::Sysvar,
    program::invoke,
    clock::Clock,
};
use borsh::{BorshDeserialize, BorshSerialize};

// Production constants
const MAX_SEED_VECTORS: usize = 64;
const FOLD_LIMIT: u32 = 24;
const SAFE_ZERO: u64 = 0;
const PHI_SCALED: u64 = 1_618_033_988;          // φ * 10^9
const PHI_SQ_SCALED: u64 = 2_618_033_988;       // φ² * 10^9
const PHI_INV_SCALED: u64 = 618_033_988;        // 1/φ * 10^9
const R_DIM_STEP: u64 = 41_421_356;             // √2/100 * 10^9 for smooth evolution
const OMEGA_DECAY: u64 = 995_000_000;           // 0.995 * 10^9 for stability
const SCALE_FACTOR: u64 = 1_000_000_000;        // 10^9 precision

// Fibonacci sequence for uniqueness verification
const FIBONACCI: [u64; 24] = [
    1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
    233, 377, 610, 987, 1597, 2584, 4181, 6765,
    10946, 17711, 28657, 46368
];

// Strand-specific initialization parameters (φ-derived)
const STRAND_PARAMS: [(u64, u64); 8] = [
    (PHI_SCALED, PHI_SQ_SCALED),           // A: φ, φ²
    (PHI_SQ_SCALED, PHI_SCALED),           // B: φ², φ
    (PHI_INV_SCALED, PHI_SCALED),          // C: 1/φ, φ
    (PHI_SCALED, PHI_INV_SCALED),          // D: φ, 1/φ
    (PHI_SQ_SCALED * 2, PHI_SCALED),       // E: 2φ², φ
    (PHI_SCALED, PHI_SQ_SCALED * 2),       // F: φ, 2φ²
    (PHI_INV_SCALED * 3, PHI_SCALED),      // G: 3/φ, φ
    (PHI_SCALED, PHI_INV_SCALED * 3),      // H: φ, 3/φ
];

#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct HDGLState {
    // Core lattice: 64 Vec4 = 256 u64 values (2KB)
    pub lattice: [u64; MAX_SEED_VECTORS * 4],
    
    // Evolution parameters
    pub r_dim_scaled: u64,          // Dimensional parameter * 10^9
    pub omega_scaled: u64,          // Frequency parameter * 10^9
    
    // State tracking
    pub fold_step: u32,             // Current fold iteration (0-24)
    pub strand_id: u8,              // Strand identifier (0-7: A-H)
    pub last_evolution: i64,        // Unix timestamp of last evolution
    
    // Verification data
    pub commitment_hash: [u8; 32],  // Keccak256 commitment for cross-chain
    pub total_evolutions: u64,      // Total number of evolutions performed
    
    // Reserved for future use
    pub reserved: [u64; 8],
}

impl Default for HDGLState {
    fn default() -> Self {
        Self {
            lattice: [SAFE_ZERO; MAX_SEED_VECTORS * 4],
            r_dim_scaled: SCALE_FACTOR / 2,  // Start at 0.5
            omega_scaled: SCALE_FACTOR,      // Start at 1.0
            fold_step: 0,
            strand_id: 0,
            last_evolution: 0,
            commitment_hash: [0u8; 32],
            total_evolutions: 0,
            reserved: [0u64; 8],
        }
    }
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum HDGLInstruction {
    /// Initialize HDGL state for a specific strand
    Initialize {
        authority: Pubkey,
        strand_id: u8,
        initial_r_dim: u64,
        initial_omega: u64,
    },
    
    /// Perform φ-delta evolution step
    FoldEvolve,
    
    /// Reset lattice to initial state (authority only)
    Reset,
    
    /// Update parameters (authority only)
    UpdateParams {
        new_r_dim: u64,
        new_omega: u64,
    },
    
    /// Generate and store cross-chain commitment
    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)?;
    
    match instruction {
        HDGLInstruction::Initialize {
            authority,
            strand_id,
            initial_r_dim,
            initial_omega,
        } => process_initialize(
            program_id,
            accounts,
            authority,
            strand_id,
            initial_r_dim,
            initial_omega,
        ),
        
        HDGLInstruction::FoldEvolve => process_fold_evolve(accounts),
        
        HDGLInstruction::Reset => process_reset(accounts),
        
        HDGLInstruction::UpdateParams { new_r_dim, new_omega } => {
            process_update_params(accounts, new_r_dim, new_omega)
        }
        
        HDGLInstruction::GenerateCommitment => process_generate_commitment(accounts),
    }
}

fn process_initialize(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    authority: Pubkey,
    strand_id: u8,
    initial_r_dim: u64,
    initial_omega: u64,
) -> ProgramResult {
    let account_info_iter = &mut accounts.iter();
    let state_account = next_account_info(account_info_iter)?;
    let payer = next_account_info(account_info_iter)?;
    let system_program = next_account_info(account_info_iter)?;
    
    // Validate inputs
    if !payer.is_signer {
        return Err(ProgramError::MissingRequiredSignature);
    }
    
    if strand_id > 7 {
        msg!("Invalid strand_id: {}. Must be 0-7", strand_id);
        return Err(ProgramError::InvalidArgument);
    }
    
    if initial_r_dim == 0 || initial_r_dim > SCALE_FACTOR {
        msg!("Invalid r_dim: {}. Must be 1-{}", initial_r_dim, SCALE_FACTOR);
        return Err(ProgramError::InvalidArgument);
    }
    
    // Create account
    let space = std::mem::size_of::<HDGLState>();
    let rent = Rent::get()?;
    let lamports = rent.minimum_balance(space);
    
    invoke(
        &system_instruction::create_account(
            payer.key,
            state_account.key,
            lamports,
            space as u64,
            program_id,
        ),
        &[payer.clone(), state_account.clone(), system_program.clone()],
    )?;
    
    // Initialize state
    let clock = Clock::get()?;
    let mut state = HDGLState {
        r_dim_scaled: initial_r_dim,
        omega_scaled: initial_omega,
        strand_id,
        last_evolution: clock.unix_timestamp,
        ..Default::default()
    };
    
    // Initialize lattice with strand-specific φ pattern
    initialize_strand_lattice(&mut state.lattice, strand_id);
    
    // Generate initial commitment
    state.commitment_hash = generate_lattice_commitment(&state);
    
    state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
    
    msg!(
        "HDGL initialized: strand={}, r_dim={}, omega={}",
        strand_id,
        initial_r_dim,
        initial_omega
    );
    
    Ok(())
}

fn process_fold_evolve(accounts: &[AccountInfo]) -> ProgramResult {
    let account_info_iter = &mut accounts.iter();
    let state_account = next_account_info(account_info_iter)?;
    
    let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
        .map_err(|_| ProgramError::InvalidAccountData)?;
    
    // Check fold limit
    if state.fold_step >= FOLD_LIMIT {
        msg!("Fold limit reached: {}/{}", state.fold_step, FOLD_LIMIT);
        return Ok(());
    }
    
    // Get current time for evolution rate limiting
    let clock = Clock::get()?;
    let time_since_last = clock.unix_timestamp - state.last_evolution;
    
    // Rate limit: minimum 10 seconds between evolutions
    if time_since_last < 10 {
        msg!("Evolution rate limited. Wait {} seconds", 10 - time_since_last);
        return Err(ProgramError::Custom(1)); // Custom error for rate limit
    }
    
    // Perform φ-delta evolution
    phi_delta_evolution(&mut state.lattice, state.fold_step, state.strand_id);
    
    // Update evolution parameters using φ-based progression
    state.r_dim_scaled = evolve_r_dim(state.r_dim_scaled, state.fold_step);
    state.omega_scaled = evolve_omega(state.omega_scaled, state.fold_step);
    
    // Increment state
    state.fold_step += 1;
    state.total_evolutions += 1;
    state.last_evolution = clock.unix_timestamp;
    
    // Generate new commitment for cross-chain verification
    state.commitment_hash = generate_lattice_commitment(&state);
    
    // Verify Fibonacci uniqueness constraint
    verify_fibonacci_constraint(&state)?;
    
    state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
    
    msg!(
        "Evolution completed: fold={}, vectors={}, r_dim={}, omega={}",
        state.fold_step,
        calculate_vector_count(state.fold_step),
        state.r_dim_scaled,
        state.omega_scaled
    );
    
    Ok(())
}

fn process_reset(accounts: &[AccountInfo]) -> ProgramResult {
    let account_info_iter = &mut accounts.iter();
    let state_account = next_account_info(account_info_iter)?;
    let authority = next_account_info(account_info_iter)?;
    
    if !authority.is_signer {
        return Err(ProgramError::MissingRequiredSignature);
    }
    
    let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
        .map_err(|_| ProgramError::InvalidAccountData)?;
    
    // Reset to initial state
    let strand_id = state.strand_id;
    state.lattice = [SAFE_ZERO; MAX_SEED_VECTORS * 4];
    state.fold_step = 0;
    state.r_dim_scaled = SCALE_FACTOR / 2;
    state.omega_scaled = SCALE_FACTOR;
    
    // Re-initialize strand pattern
    initialize_strand_lattice(&mut state.lattice, strand_id);
    state.commitment_hash = generate_lattice_commitment(&state);
    
    let clock = Clock::get()?;
    state.last_evolution = clock.unix_timestamp;
    
    state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
    
    msg!("HDGL state reset for strand {}", strand_id);
    Ok(())
}

fn process_update_params(
    accounts: &[AccountInfo],
    new_r_dim: u64,
    new_omega: u64,
) -> ProgramResult {
    let account_info_iter = &mut accounts.iter();
    let state_account = next_account_info(account_info_iter)?;
    let authority = next_account_info(account_info_iter)?;
    
    if !authority.is_signer {
        return Err(ProgramError::MissingRequiredSignature);
    }
    
    // Validate new parameters
    if new_r_dim == 0 || new_r_dim > SCALE_FACTOR {
        return Err(ProgramError::InvalidArgument);
    }
    if new_omega == 0 || new_omega > SCALE_FACTOR * 10 {
        return Err(ProgramError::InvalidArgument);
    }
    
    let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
        .map_err(|_| ProgramError::InvalidAccountData)?;
    
    state.r_dim_scaled = new_r_dim;
    state.omega_scaled = new_omega;
    state.commitment_hash = generate_lattice_commitment(&state);
    
    state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
    
    msg!("Parameters updated: r_dim={}, omega={}", new_r_dim, new_omega);
    Ok(())
}

fn process_generate_commitment(accounts: &[AccountInfo]) -> ProgramResult {
    let account_info_iter = &mut accounts.iter();
    let state_account = next_account_info(account_info_iter)?;
    
    let mut state = HDGLState::try_from_slice(&state_account.data.borrow())
        .map_err(|_| ProgramError::InvalidAccountData)?;
    
    // Regenerate commitment hash for current state
    state.commitment_hash = generate_lattice_commitment(&state);
    
    state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
    
    msg!("Commitment regenerated");
    Ok(())
}

/// Initialize lattice with strand-specific φ-derived patterns
fn initialize_strand_lattice(lattice: &mut [u64; MAX_SEED_VECTORS * 4], strand_id: u8) {
    let (param_a, param_b) = STRAND_PARAMS[strand_id as usize];
    
    // Set initial excitation pattern based on strand
    match strand_id {
        0 => {
            // Strand A: Golden spiral initialization
            lattice[0] = param_a;
            lattice[1] = param_b;
            lattice[2] = PHI_SCALED;
            lattice[3] = PHI_INV_SCALED;
        }
        1 => {
            // Strand B: Inverse golden spiral
            lattice[4] = param_a;
            lattice[5] = param_b;
            lattice[6] = PHI_INV_SCALED;
            lattice[7] = PHI_SCALED;
        }
        2..=7 => {
            // Strands C-H: Fibonacci-offset patterns
            let offset = (strand_id as usize) * 8;
            for i in 0..4 {
                let idx = offset + i;
                if idx < lattice.len() {
                    lattice[idx] = param_a.saturating_add(FIBONACCI[i] * PHI_INV_SCALED);
                }
            }
        }
        _ => {} // Should never reach here due to validation
    }
}

/// Perform φ-delta evolution on the lattice
fn phi_delta_evolution(
    lattice: &mut [u64; MAX_SEED_VECTORS * 4],
    fold_step: u32,
    strand_id: u8,
) {
    let evolution_radius = calculate_evolution_radius(fold_step);
    let strand_offset = (strand_id as usize) * 8;
    
    // Apply φ-delta transformation across expanding radius
    for i in 0..evolution_radius.min(lattice.len()) {
        let base_idx = (strand_offset + i) % lattice.len();
        
        // φ-delta computation with overflow protection
        let current = lattice[base_idx];
        let phi_delta = compute_phi_delta(current, fold_step, i);
        
        lattice[base_idx] = current.saturating_add(phi_delta);
        
        // Cross-strand resonance for advanced steps
        if fold_step > 8 {
            let resonance_idx = (base_idx + PHI_SCALED as usize) % lattice.len();
            let resonance_delta = phi_delta.saturating_mul(PHI_INV_SCALED) / SCALE_FACTOR;
            lattice[resonance_idx] = lattice[resonance_idx].saturating_add(resonance_delta);
        }
    }
    
    // Normalization to prevent unbounded growth
    normalize_lattice(lattice, fold_step);
}

/// Calculate evolution radius based on fold step
fn calculate_evolution_radius(fold_step: u32) -> usize {
    let base_radius = 4_usize;
    let expansion = (fold_step as usize).min(6); // Cap expansion
    base_radius * (1 << expansion) // Exponential growth: 4, 8, 16, 32, 64, 128, 256
}

/// Compute φ-delta value for lattice evolution
fn compute_phi_delta(current_value: u64, fold_step: u32, index: usize) -> u64 {
    // Base φ-delta using golden ratio properties
    let base_delta = PHI_SQ_SCALED;
    
    // Modulate by fold step using Fibonacci sequence
    let fib_mod = FIBONACCI[fold_step.min(23) as usize];
    let step_modulation = base_delta.saturating_mul(fib_mod) / SCALE_FACTOR;
    
    // Add positional variance
    let pos_variance = (index as u64).saturating_mul(PHI_INV_SCALED) % SCALE_FACTOR;
    
    // Combine with current value influence (prevents stagnation)
    let value_influence = current_value.saturating_mul(PHI_INV_SCALED) / (SCALE_FACTOR * 100);
    
    step_modulation.saturating_add(pos_variance).saturating_add(value_influence)
}

/// Normalize lattice to prevent overflow and maintain stability
fn normalize_lattice(lattice: &mut [u64; MAX_SEED_VECTORS * 4], fold_step: u32) {
    if fold_step < 4 {
        return; // Skip normalization for early steps
    }
    
    // Find maximum value
    let max_val = lattice.iter().max().unwrap_or(&0);
    
    // Apply normalization if needed (prevent values > 10^12)
    if *max_val > SCALE_FACTOR * 1000 {
        let normalization_factor = SCALE_FACTOR * 500 / max_val;
        
        for value in lattice.iter_mut() {
            *value = value.saturating_mul(normalization_factor) / SCALE_FACTOR;
        }
    }
}

/// Evolve r_dim parameter using φ-based progression
fn evolve_r_dim(current_r_dim: u64, fold_step: u32) -> u64 {
    let step_increment = R_DIM_STEP.saturating_mul(PHI_INV_SCALED) / SCALE_FACTOR;
    let fibonacci_mod = FIBONACCI[fold_step.min(23) as usize];
    let adjusted_increment = step_increment.saturating_mul(fibonacci_mod) / 100;
    
    current_r_dim.saturating_add(adjusted_increment).min(SCALE_FACTOR)
}

/// Evolve omega parameter using φ-based decay
fn evolve_omega(current_omega: u64, fold_step: u32) -> u64 {
    // Apply decay with φ-modulation
    let decay_factor = OMEGA_DECAY.saturating_add(
        PHI_INV_SCALED.saturating_mul(FIBONACCI[fold_step.min(23) as usize]) / SCALE_FACTOR
    );
    
    current_omega.saturating_mul(decay_factor) / SCALE_FACTOR
}

/// Calculate current vector count based on fold step
fn calculate_vector_count(fold_step: u32) -> u64 {
    2_u64.saturating_pow(fold_step.min(20)) // Cap to prevent overflow
}

/// Verify Fibonacci uniqueness constraint
fn verify_fibonacci_constraint(state: &HDGLState) -> ProgramResult {
    let expected_fib = FIBONACCI[state.fold_step.min(23) as usize];
    let lattice_sum: u128 = state.lattice[..8].iter().map(|&x| x as u128).sum();
    let sum_mod = (lattice_sum % (expected_fib as u128)) as u64;
    
    // Constraint: lattice sum should have specific modular relationship with Fibonacci
    if sum_mod == 0 && expected_fib > 1 {
        msg!("Fibonacci constraint violated at step {}", state.fold_step);
        return Err(ProgramError::Custom(2)); // Custom constraint violation error
    }
    
    Ok(())
}

/// Generate Keccak256 commitment hash for cross-chain verification
fn generate_lattice_commitment(state: &HDGLState) -> [u8; 32] {
    use solana_program::keccak;
    
    let mut commitment_data = Vec::with_capacity(32 * 10);
    
    // Include first 8 lattice values (D_slots equivalent)
    for &value in state.lattice[..8].iter() {
        commitment_data.extend_from_slice(&value.to_be_bytes());
    }
    
    // Include evolution parameters
    commitment_data.extend_from_slice(&state.r_dim_scaled.to_be_bytes());
    commitment_data.extend_from_slice(&state.omega_scaled.to_be_bytes());
    commitment_data.extend_from_slice(&state.fold_step.to_be_bytes());
    commitment_data.extend_from_slice(&[state.strand_id]);
    
    let hash = keccak::hash(&commitment_data);
    hash.to_bytes()
}

// Error handling
#[derive(Debug)]
pub enum HDGLError {
    RateLimited = 1,
    ConstraintViolation = 2,
    InvalidStrand = 3,
    ParameterOutOfRange = 4,
}

impl From<HDGLError> for ProgramError {
    fn from(e: HDGLError) -> Self {
        ProgramError::Custom(e as u32)
    }
}