// src/monitoring/service.rs
use super::events::*;
use super::metrics::*;
use super::logger::SessionLogger;
use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::{mpsc, RwLock};
use chrono::Local;

const MAX_LOGS: usize = 100;

/// Monitoring service that collects events and maintains state
pub struct MonitoringService {
    state: Arc<RwLock<MonitoringState>>,
    config: ConfigSnapshot,
    logs: Arc<RwLock<VecDeque<LogEntry>>>,
    session_logger: Arc<SessionLogger>,
    event_rx: mpsc::UnboundedReceiver<MonitoringEvent>,
}

impl MonitoringService {
    pub fn new(
        config: ConfigSnapshot,
        event_rx: mpsc::UnboundedReceiver<MonitoringEvent>,
        log_dir: std::path::PathBuf,
    ) -> Self {
        Self {
            state: Arc::new(RwLock::new(MonitoringState::new())),
            config,
            logs: Arc::new(RwLock::new(VecDeque::with_capacity(MAX_LOGS))),
            session_logger: Arc::new(SessionLogger::new(log_dir)),
            event_rx,
        }
    }

    /// Get current dashboard state for web clients
    pub async fn get_dashboard_state(&self) -> DashboardState {
        let state = self.state.read().await;
        let logs = self.logs.read().await;
        state.to_dashboard_state(
            self.config.clone(),
            logs.iter().cloned().collect(),
        )
    }

    /// Main event processing loop
    pub async fn run(mut self) {
        while let Some(event) = self.event_rx.recv().await {
            self.process_event(event).await;
        }
    }

    async fn process_event(&self, event: MonitoringEvent) {
        match event {
            MonitoringEvent::PoolInitialized { pool_size, with_proxy, with_rotation } => {
                self.log_info(format!(
                    "Pool initialized: {} instances, proxy={}, rotation={}",
                    pool_size, with_proxy, with_rotation
                )).await;
            }

            MonitoringEvent::InstanceCreated { instance_id, max_tasks, proxy } => {
                let mut state = self.state.write().await;
                state.instances.insert(
                    instance_id,
                    InstanceState {
                        id: instance_id,
                        status: InstanceStatus::Idle,
                        current_task: None,
                        tasks_current_session: 0,
                        tasks_max: max_tasks,
                        session_requests: 0,
                        total_requests: 0,
                        success_count: 0,
                        failure_count: 0,
                        connected_proxy: proxy.clone(),
                        last_activity: Instant::now(),
                        yahoo_requests: 0,
                        yahoo_success: 0,
                        yahoo_failures: 0,
                        yahoo_current_requests: 0,
                        yahoo_max_requests: 0,
                        yahoo_last_endpoint: None,
                        yahoo_last_symbol: None,
                    },
                );

                if let Some(proxy_info) = proxy {
                    state.proxies.entry(proxy_info.container_name.clone()).or_insert_with(|| {
                        ProxyState {
                            container_name: proxy_info.container_name.clone(),
                            ip_address: proxy_info.ip_address.clone(),
                            port: proxy_info.port,
                            status: ProxyStatus::Connected,
                            instances_using: vec![instance_id],
                        }
                    }).instances_using.push(instance_id);
                }

                self.log_info(format!("Instance #{} created", instance_id)).await;
            }

            MonitoringEvent::InstanceStatusChanged { instance_id, status } => {
                let mut state = self.state.write().await;
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.status = match status {
                        InstanceStatusChange::Idle => InstanceStatus::Idle,
                        InstanceStatusChange::Active => InstanceStatus::Active,
                        InstanceStatusChange::Renewing => InstanceStatus::Renewing,
                        InstanceStatusChange::Error(_) => InstanceStatus::Error,
                    };
                    inst.last_activity = Instant::now();
                }
            }

            MonitoringEvent::InstanceSelected { instance_id, half } => {
                self.log_info(format!("Instance #{} selected (half {})", instance_id, half)).await;
            }

            MonitoringEvent::TaskStarted { instance_id, url } => {
                let mut state = self.state.write().await;
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.status = InstanceStatus::Active;
                    inst.current_task = Some(url.clone());
                    inst.last_activity = Instant::now();
                }
                state.global.total_requests += 1;

                self.log_info(format!("Instance #{} started task: {}", instance_id, url)).await;
            }

            MonitoringEvent::TaskCompleted { instance_id, success, duration_ms, error } => {
                let mut state = self.state.write().await;
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.current_task = None;
                    inst.status = InstanceStatus::Idle;
                    inst.total_requests += 1;
                    inst.last_activity = Instant::now();

                    if success {
                        inst.success_count += 1;
                        state.global.successful_requests += 1;
                    } else {
                        inst.failure_count += 1;
                        state.global.failed_requests += 1;
                    }
                }

                if success {
                    self.log_info(format!(
                        "Instance #{} completed task in {}ms",
                        instance_id, duration_ms
                    )).await;
                } else {
                    self.log_error(format!(
                        "Instance #{} failed task: {}",
                        instance_id,
                        error.unwrap_or_else(|| "unknown error".to_string())
                    )).await;
                }
            }

            MonitoringEvent::NavigationTimeout { instance_id, url } => {
                let mut state = self.state.write().await;
                state.global.navigation_timeouts += 1;
                
                self.log_warn(format!(
                    "Instance #{} navigation timeout: {}",
                    instance_id, url
                )).await;
            }

            MonitoringEvent::BotDetectionTriggered { instance_id, url } => {
                let mut state = self.state.write().await;
                state.global.bot_detection_hits += 1;
                
                self.log_warn(format!(
                    "Instance #{} bot detection triggered: {}",
                    instance_id, url
                )).await;
            }

            MonitoringEvent::SessionStarted { instance_id, proxy } => {
                let mut state = self.state.write().await;
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.session_requests = 0;
                    inst.tasks_current_session = 0;
                    inst.connected_proxy = proxy;
                    inst.last_activity = Instant::now();
                }

                self.log_info(format!("Instance #{} started new session", instance_id)).await;
            }

            MonitoringEvent::SessionRenewed { instance_id, old_request_count, reason, new_proxy } => {
                // Log the completed session
                let session_summary = {
                    let state = self.state.read().await;
                    if let Some(inst) = state.instances.get(&instance_id) {
                        Some(SessionSummary {
                            instance_id,
                            session_start: "N/A".to_string(),
                            session_end: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
                            duration_seconds: 0,
                            total_requests: old_request_count,
                            successful_requests: inst.success_count,
                            failed_requests: inst.failure_count,
                            proxy_info: inst.connected_proxy.clone(),
                            renewal_reason: reason.to_string(),
                        })
                    } else {
                        None
                    }
                };

                if let Some(summary) = session_summary {
                    self.session_logger.log_session(&summary).await;
                }

                // Update state for new session
                let mut state = self.state.write().await;
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.session_requests = 0;
                    inst.tasks_current_session = 0;
                    inst.connected_proxy = new_proxy;
                    inst.last_activity = Instant::now();
                }
                state.global.session_renewals += 1;

                self.log_info(format!(
                    "Instance #{} renewed session (reason: {}, {} requests)",
                    instance_id, reason, old_request_count
                )).await;
            }

            MonitoringEvent::SessionRequestIncremented { instance_id, new_count } => {
                let mut state = self.state.write().await;
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.session_requests = new_count;
                    inst.last_activity = Instant::now();
                }
            }

            MonitoringEvent::ProxyConnected { container_name, ip_address, port } => {
                let mut state = self.state.write().await;
                state.proxies.insert(
                    container_name.clone(),
                    ProxyState {
                        container_name: container_name.clone(),
                        ip_address: ip_address.clone(),
                        port,
                        status: ProxyStatus::Connected,
                        instances_using: vec![],
                    },
                );

                self.log_info(format!(
                    "Proxy {} connected: {}:{}",
                    container_name, ip_address, port
                )).await;
            }

            MonitoringEvent::ProxyFailed { container_name, error } => {
                let mut state = self.state.write().await;
                if let Some(proxy) = state.proxies.get_mut(&container_name) {
                    proxy.status = ProxyStatus::Disconnected;
                }
                state.global.proxy_failures += 1;

                self.log_error(format!(
                    "Proxy {} failed: {}",
                    container_name, error
                )).await;
            }

            MonitoringEvent::ProxyRotated { instance_id, old_proxy, new_proxy } => {
                self.log_info(format!(
                    "Instance #{} rotated proxy: {} -> {}",
                    instance_id,
                    old_proxy.unwrap_or_else(|| "none".to_string()),
                    new_proxy
                )).await;
            }

            MonitoringEvent::RotationTriggered { reason } => {
                let mut state = self.state.write().await;
                state.global.rotation_events += 1;

                self.log_info(format!("Pool rotation triggered: {}", reason)).await;
            }

            // Yahoo API Events
            MonitoringEvent::YahooRequestStarted { instance_id, endpoint, symbol } => {
                let mut state = self.state.write().await;
                
                // Update global Yahoo stats
                state.global.total_yahoo_requests += 1;
                
                // Update instance stats
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.yahoo_requests += 1;
                    inst.yahoo_current_requests += 1;
                    inst.yahoo_last_endpoint = Some(endpoint.clone());
                    inst.yahoo_last_symbol = symbol.clone();
                    inst.last_activity = Instant::now();
                }
                
                // Update Yahoo client stats
                if let Some(client) = state.yahoo_clients.get_mut(&instance_id) {
                    client.requests_total += 1;
                    client.current_requests += 1;
                    client.last_activity = Instant::now();
                }
                
                self.log_info(format!(
                    "YahooClient[{}] started request: {} {}",
                    instance_id,
                    endpoint,
                    symbol.unwrap_or_else(|| "search".to_string())
                )).await;
            }
            
            MonitoringEvent::YahooRequestCompleted { instance_id, success, duration_ms, error } => {
                let mut state = self.state.write().await;
                
                // Update global Yahoo stats
                if success {
                    state.global.successful_yahoo_requests += 1;
                } else {
                    state.global.failed_yahoo_requests += 1;
                }
                
                // Update instance stats
                if let Some(inst) = state.instances.get_mut(&instance_id) {
                    inst.yahoo_current_requests = inst.yahoo_current_requests.saturating_sub(1);
                    if success {
                        inst.yahoo_success += 1;
                    } else {
                        inst.yahoo_failures += 1;
                    }
                    inst.last_activity = Instant::now();
                }
                
                // Update Yahoo client stats
                if let Some(client) = state.yahoo_clients.get_mut(&instance_id) {
                    client.current_requests = client.current_requests.saturating_sub(1);
                    if success {
                        client.requests_successful += 1;
                    } else {
                        client.requests_failed += 1;
                    }
                    client.last_activity = Instant::now();
                }
                
                if success {
                    self.log_info(format!(
                        "YahooClient[{}] completed request in {}ms",
                        instance_id, duration_ms
                    )).await;
                } else {
                    self.log_error(format!(
                        "YahooClient[{}] failed request in {}ms: {}",
                        instance_id,
                        duration_ms,
                        error.unwrap_or_else(|| "unknown error".to_string())
                    )).await;
                }
            }
            
            MonitoringEvent::YahooBatchRequestStarted { count, symbols, endpoint } => {
                let mut state = self.state.write().await;
                state.global.yahoo_batch_requests += 1;
                
                self.log_info(format!(
                    "Yahoo batch request started: {} symbols, endpoint: {}",
                    count, endpoint
                )).await;
                
                if !symbols.is_empty() {
                    self.log_debug(format!(
                        "Batch symbols: {}",
                        symbols.join(", ")
                    )).await;
                }
            }
            
            MonitoringEvent::YahooBatchRequestCompleted { successful, failed, total, duration_ms } => {
                let success_rate = if total > 0 {
                    (successful as f64 / total as f64) * 100.0
                } else {
                    0.0
                };
                
                self.log_info(format!(
                    "Yahoo batch completed: {}/{} successful ({:.1}%) in {}ms",
                    successful, total, success_rate, duration_ms
                )).await;
            }
            
            MonitoringEvent::YahooClientCreated { instance_id, has_proxy, max_requests } => {
                let mut state = self.state.write().await;
                state.global.yahoo_client_count += 1;
                
                state.yahoo_clients.insert(
                    instance_id,
                    YahooClientState {
                        instance_id,
                        requests_total: 0,
                        requests_successful: 0,
                        requests_failed: 0,
                        current_requests: 0,
                        max_requests,
                        has_proxy,
                        last_activity: Instant::now(),
                        proxy_info: None,
                    },
                );
                
                self.log_info(format!(
                    "YahooClient[{}] created (proxy: {}, max requests: {})",
                    instance_id, has_proxy, max_requests
                )).await;
            }
            
            MonitoringEvent::YahooClientReset { instance_id, previous_requests, reason } => {
                let mut state = self.state.write().await;
                state.global.yahoo_session_renewals += 1;
                
                if let Some(client) = state.yahoo_clients.get_mut(&instance_id) {
                    client.current_requests = 0;
                    client.last_activity = Instant::now();
                }
                
                self.log_info(format!(
                    "YahooClient[{}] reset (had {} requests, reason: {})",
                    instance_id, previous_requests, reason
                )).await;
            }

            MonitoringEvent::LogMessage { level, message } => {
                match level {
                    crate::monitoring::events::LogLevel::Info => self.log_info(message).await,
                    crate::monitoring::events::LogLevel::Warn => self.log_warn(message).await,
                    crate::monitoring::events::LogLevel::Error => self.log_error(message).await,
                }
            }
        }
    }

    async fn log_info(&self, message: String) {
        self.add_log(LogEntry {
            timestamp: Local::now().format("%H:%M:%S").to_string(),
            level: super::metrics::LogLevel::Info,
            message,
        }).await;
    }

    async fn log_warn(&self, message: String) {
        self.add_log(LogEntry {
            timestamp: Local::now().format("%H:%M:%S").to_string(),
            level: super::metrics::LogLevel::Warn,
            message,
        }).await;
    }

    async fn log_error(&self, message: String) {
        self.add_log(LogEntry {
            timestamp: Local::now().format("%H:%M:%S").to_string(),
            level: super::metrics::LogLevel::Error,
            message,
        }).await;
    }

    async fn log_debug(&self, message: String) {
        // Only log debug if DEBUG_LOGGING is enabled
        if std::env::var("DEBUG_LOGGING").is_ok() {
            self.add_log(LogEntry {
                timestamp: Local::now().format("%H:%M:%S").to_string(),
                level: super::metrics::LogLevel::Info,
                message: format!("[DEBUG] {}", message),
            }).await;
        }
    }

    async fn add_log(&self, entry: LogEntry) {
        let mut logs = self.logs.write().await;
        if logs.len() >= MAX_LOGS {
            logs.pop_front();
        }
        logs.push_back(entry);
    }
}

/// Handle for emitting monitoring events
#[derive(Clone)]
pub struct MonitoringHandle {
    tx: mpsc::UnboundedSender<MonitoringEvent>,
}

impl MonitoringHandle {
    pub fn new(tx: mpsc::UnboundedSender<MonitoringEvent>) -> Self {
        Self { tx }
    }

    /// Emit a monitoring event (non-blocking)
    pub fn emit(&self, event: MonitoringEvent) {
        // Ignore send errors (monitoring should never block application)
        let _ = self.tx.send(event);
    }
}