//! Proof Submitter - Submits storage proofs to L1
//!
//! Monitors L1 for challenges and submits proofs.
//! Failed proofs result in slashing of provider stake.

use crate::proof::{Challenge, StorageProof};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Challenge status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChallengeStatus {
    /// Challenge received, generating proof
    Pending,
    /// Proof generated, awaiting submission
    ProofReady,
    /// Proof submitted to L1
    Submitted,
    /// Proof verified on L1
    Verified,
    /// Proof failed verification
    Failed,
    /// Challenge expired (missed deadline)
    Expired,
}

/// A tracked challenge
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackedChallenge {
    /// The challenge
    pub challenge: Challenge,
    /// Current status
    pub status: ChallengeStatus,
    /// Generated proof (if ready)
    pub proof: Option<StorageProof>,
    /// L1 block when challenge was received
    pub received_block: u64,
    /// L1 block deadline for submission
    pub deadline_block: u64,
    /// Transaction hash if submitted
    pub tx_hash: Option<[u8; 32]>,
    /// Error message if failed
    pub error: Option<String>,
}

/// Proof submission configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProverConfig {
    /// L1 RPC endpoint
    pub l1_rpc: String,
    /// Node private key for signing
    pub node_key: [u8; 32],
    /// Maximum gas price (in wei)
    pub max_gas_price: u64,
    /// Proof submission deadline buffer (blocks before deadline)
    pub deadline_buffer: u64,
}

impl Default for ProverConfig {
    fn default() -> Self {
        Self {
            l1_rpc: "http://localhost:8545".to_string(),
            node_key: [0u8; 32],
            max_gas_price: 100_000_000_000, // 100 gwei
            deadline_buffer: 10,
        }
    }
}

/// Proof submitter service
pub struct ProofSubmitter {
    /// Configuration
    config: ProverConfig,
    /// Active challenges being processed
    challenges: HashMap<[u8; 32], TrackedChallenge>,
    /// Proof submission statistics
    stats: ProverStats,
}

/// Prover statistics
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProverStats {
    /// Total challenges received
    pub challenges_received: u64,
    /// Proofs submitted
    pub proofs_submitted: u64,
    /// Proofs verified
    pub proofs_verified: u64,
    /// Proofs failed
    pub proofs_failed: u64,
    /// Challenges expired (missed deadline)
    pub challenges_expired: u64,
    /// Total gas spent
    pub total_gas_spent: u64,
}

impl ProofSubmitter {
    /// Create a new proof submitter
    pub fn new(config: ProverConfig) -> Self {
        Self {
            config,
            challenges: HashMap::new(),
            stats: ProverStats::default(),
        }
    }

    /// Start monitoring for challenges
    pub async fn start(&mut self) -> Result<()> {
        // TODO: Subscribe to L1 events for storage challenges
        // - Listen for ChallengeIssued events
        // - Track deadline for each challenge
        // - Generate and submit proofs before deadline

        Ok(())
    }

    /// Stop the prover
    pub async fn stop(&mut self) -> Result<()> {
        // TODO: Complete pending proof submissions
        Ok(())
    }

    /// Add a challenge to track
    pub fn add_challenge(&mut self, challenge: Challenge, current_block: u64, deadline_block: u64) {
        let challenge_id = self.challenge_id(&challenge);

        let tracked = TrackedChallenge {
            challenge,
            status: ChallengeStatus::Pending,
            proof: None,
            received_block: current_block,
            deadline_block,
            tx_hash: None,
            error: None,
        };

        self.challenges.insert(challenge_id, tracked);
        self.stats.challenges_received += 1;
    }

    /// Set proof for a challenge
    pub fn set_proof(&mut self, challenge: &Challenge, proof: StorageProof) -> Result<()> {
        let challenge_id = self.challenge_id(challenge);

        let tracked = self.challenges.get_mut(&challenge_id).ok_or_else(|| {
            Error::Internal("Challenge not found".to_string())
        })?;

        tracked.proof = Some(proof);
        tracked.status = ChallengeStatus::ProofReady;

        Ok(())
    }

    /// Submit a proof to L1
    pub async fn submit_proof(&mut self, challenge: &Challenge) -> Result<[u8; 32]> {
        let challenge_id = self.challenge_id(challenge);

        let tracked = self.challenges.get_mut(&challenge_id).ok_or_else(|| {
            Error::Internal("Challenge not found".to_string())
        })?;

        if tracked.proof.is_none() {
            return Err(Error::Internal("Proof not ready".to_string()));
        }

        // TODO: Submit proof transaction to L1
        // 1. Encode proof as L1 transaction data
        // 2. Sign with node key
        // 3. Submit to L1 RPC
        // 4. Wait for transaction receipt

        // Placeholder tx hash
        let tx_hash = *blake3::hash(challenge_id.as_slice()).as_bytes();

        tracked.tx_hash = Some(tx_hash);
        tracked.status = ChallengeStatus::Submitted;
        self.stats.proofs_submitted += 1;

        Ok(tx_hash)
    }

    /// Mark a challenge as verified
    pub fn mark_verified(&mut self, challenge: &Challenge) {
        let challenge_id = self.challenge_id(challenge);

        if let Some(tracked) = self.challenges.get_mut(&challenge_id) {
            tracked.status = ChallengeStatus::Verified;
            self.stats.proofs_verified += 1;
        }
    }

    /// Mark a challenge as failed
    pub fn mark_failed(&mut self, challenge: &Challenge, error: String) {
        let challenge_id = self.challenge_id(challenge);

        if let Some(tracked) = self.challenges.get_mut(&challenge_id) {
            tracked.status = ChallengeStatus::Failed;
            tracked.error = Some(error);
            self.stats.proofs_failed += 1;
        }
    }

    /// Check for expired challenges
    pub fn check_expired(&mut self, current_block: u64) -> Vec<[u8; 32]> {
        let mut expired = Vec::new();

        for (id, tracked) in &mut self.challenges {
            if tracked.status == ChallengeStatus::Pending
                || tracked.status == ChallengeStatus::ProofReady
            {
                if current_block >= tracked.deadline_block {
                    tracked.status = ChallengeStatus::Expired;
                    self.stats.challenges_expired += 1;
                    expired.push(*id);
                }
            }
        }

        expired
    }

    /// Get pending challenges that need proofs
    pub fn pending_challenges(&self) -> Vec<&Challenge> {
        self.challenges
            .values()
            .filter(|t| t.status == ChallengeStatus::Pending)
            .map(|t| &t.challenge)
            .collect()
    }

    /// Get challenges ready for submission
    pub fn ready_for_submission(&self) -> Vec<&Challenge> {
        self.challenges
            .values()
            .filter(|t| t.status == ChallengeStatus::ProofReady)
            .map(|t| &t.challenge)
            .collect()
    }

    /// Get prover statistics
    pub fn stats(&self) -> &ProverStats {
        &self.stats
    }

    /// Generate unique ID for a challenge
    fn challenge_id(&self, challenge: &Challenge) -> [u8; 32] {
        let mut data = Vec::new();
        data.extend_from_slice(&challenge.block_hash);
        data.extend_from_slice(&challenge.epoch.to_le_bytes());
        data.extend_from_slice(&challenge.cid.digest);
        data.extend_from_slice(&challenge.chunk_index.to_le_bytes());
        *blake3::hash(&data).as_bytes()
    }

    /// Clean up old completed/failed challenges
    pub fn cleanup(&mut self, max_age_blocks: u64, current_block: u64) {
        self.challenges.retain(|_, tracked| {
            match tracked.status {
                ChallengeStatus::Verified | ChallengeStatus::Failed | ChallengeStatus::Expired => {
                    current_block - tracked.received_block < max_age_blocks
                }
                _ => true,
            }
        });
    }
}
