use serde::{Deserialize, Serialize};
use steel::*;

use crate::state::well_pda;

use super::{OilAccount, Auction};

/// Well account (one per well)
/// PDA: [WELL, well_id]
/// Tracks current auction state for a well
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
pub struct Well {
    /// Well ID (0-3) - which well this is for
    pub well_id: u64,
    
    /// Current epoch ID (increments each auction: 0, 1, 2, 3, etc.)
    /// Starts at 0, increments when bid happens
    pub epoch_id: u64,
    
    /// Current bidder/owner (Pubkey::default() if unowned)
    pub current_bidder: Pubkey,
    
    /// Initial price for current epoch (in lamports)
    /// Doubles from current price when bid happens
    pub init_price: u64,
    
    /// Mining per second (MPS) - current mining rate (OIL per second, in atomic units)
    /// This is the base rate adjusted for halvings
    pub mps: u64,
    
    /// Epoch start time (timestamp when current epoch started)
    pub epoch_start_time: u64,
    
    /// Accumulated OIL mined by current owner (not yet claimed)
    pub accumulated_oil: u64,
    
    /// Last time accumulated_oil was updated
    pub last_update_time: u64,
    
    /// Number of halvings that have occurred (for rate calculation)
    pub halving_count: u64,
    
    /// Total OIL ever mined from this well (lifetime)
    pub lifetime_oil_mined: u64,
    
    /// Flag indicating if owned by pool (1) or solo owner (0)
    /// When pool wins (pool_total >= current_price), this is set to 1
    pub is_pool_owned: u64,
    
    /// Total OIL mined by current operator (doesn't reset when claimed, only when ownership changes)
    /// Repurposed from buffer_a
    pub operator_total_oil_mined: u64,
    
    /// Last epoch ID that was synced (similar to checkpoint_id in Driller)
    /// Used to enforce sync_auction_state before set_bid (like checkpoint before deploy)
    /// Repurposed from buffer_b
    pub last_synced_epoch_id: u64,
    
    /// Accumulated OIL from a pool that was outbid by a solo bidder
    /// When a solo bid outbids a pool, the pool's accumulated_oil is stored here
    /// Pool contributors can claim their proportional share from this amount
    /// Repurposed from buffer_c
    pub pool_accumulated_oil: u64,
    
    /// Original pool_total from when a pool was outbid (for calculating proportional shares)
    /// When a solo bid outbids a pool, the original pool_total is stored here
    /// Repurposed from buffer_d
    pub outbid_pool_total: u64,
    
    /// 86% SOL from bidder when a pool is outbid (for proportional distribution to pool contributors)
    /// When a solo bid outbids a pool, the 86% SOL amount is stored here
    /// Pool contributors can claim their proportional share from this amount
    pub pool_outbid_sol: u64,
}

impl Well {
    pub fn pda(well_id: u64) -> (Pubkey, u8) {
        well_pda(well_id)
    }

    /// Calculate current price for this epoch (Dutch auction - price decreases over time)
    pub fn current_price(&self, auction: &Auction, clock: &Clock) -> u64 {
        // If well has no owner (never been bid on), show starting price
        use solana_program::pubkey::Pubkey;
        if self.current_bidder == Pubkey::default() && self.is_pool_owned == 0 {
            return self.init_price; // Return starting price for unowned wells
        }
        
        let elapsed = clock.unix_timestamp.saturating_sub(self.epoch_start_time as i64);
        let duration = auction.auction_duration_seconds as i64;
        
        if elapsed >= duration {
            return 0; // Auction expired, free to claim
        }
        
        // Linear decay: price = init_price * (1 - elapsed / duration)
        // Applies to both solo-owned and pool-owned wells
        let remaining = duration - elapsed;
        (self.init_price as u128 * remaining as u128 / duration as u128) as u64
    }

    /// Update accumulated OIL for this epoch state
    pub fn update_accumulated_oil(&mut self, clock: &Clock) {
        // Skip if no owner
        if self.current_bidder == Pubkey::default() && self.is_pool_owned == 0 {
            return;
        }
        
        let last_update = self.last_update_time as i64;
        let elapsed = clock.unix_timestamp.saturating_sub(last_update);
        if elapsed <= 0 {
            return;
        }
        
        // Calculate OIL mined: rate * time
        let oil_mined = self.mps
            .checked_mul(elapsed as u64)
            .unwrap_or(0);
        
        self.accumulated_oil = self.accumulated_oil
            .checked_add(oil_mined)
            .unwrap_or(u64::MAX);
        
        self.lifetime_oil_mined = self.lifetime_oil_mined
            .checked_add(oil_mined)
            .unwrap_or(u64::MAX);
        
        // Track total mined by current operator (persists even after claiming)
        self.operator_total_oil_mined = self.operator_total_oil_mined
            .checked_add(oil_mined)
            .unwrap_or(u64::MAX);
        
        self.last_update_time = clock.unix_timestamp as u64;
    }

    /// Check and apply halving if needed, updating mining rate
    /// Uses less aggressive halving: 25% reduction (multiply by 0.75) instead of 50% (divide by 2)
    /// This is more sustainable for the lower base rates (2.1 OIL/s total vs Macaron's 4.0 OIL/s)
    pub fn check_and_apply_halving(&mut self, auction: &mut Auction, clock: &Clock) {
        let current_time = clock.unix_timestamp as u64;
        let time_since_last_halving = current_time.saturating_sub(auction.last_halving_time);
        
        if time_since_last_halving >= auction.halving_period_seconds {
            // Calculate number of halvings to apply
            let halvings_to_apply = time_since_last_halving / auction.halving_period_seconds;
            
            // Apply less aggressive halving: 25% reduction (multiply by 0.75) instead of 50% (divide by 2)
            // This maintains profitability longer while still creating scarcity over time
            // Formula: new_rate = old_rate * 0.75 = old_rate * 3 / 4
            for _ in 0..halvings_to_apply {
                self.mps = (self.mps * 3) / 4; // 25% reduction (multiply by 0.75)
                self.halving_count += 1;
            }
            
            // Update last halving time (only update once per halving event)
            // Note: This will be called for each epoch state, but we only update once
            // In practice, we might want to check this globally
            auction.last_halving_time += halvings_to_apply * auction.halving_period_seconds;
        }
    }
}

account!(OilAccount, Well);

