use std::fs::{self, File, OpenOptions};
use std::io::{Write, Read, BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tracing::{Event, Subscriber};
use tracing_subscriber::layer::Context;
use tracing_subscriber::Layer;
use chrono::Utc;
use anyhow::Result;
use tokio::sync::broadcast;

/// Manages temporary log files for workflow requests.
#[derive(Clone)]
pub struct LogBufferManager {
    root_path: PathBuf,
}

impl LogBufferManager {
    pub fn new<P: AsRef<Path>>(root_path: P) -> Self {
        let path = root_path.as_ref().to_path_buf();
        if !path.exists() {
            let _ = fs::create_dir_all(&path);
        }
        Self { root_path: path }
    }

    fn get_log_path(&self, request_id: &str) -> PathBuf {
        self.root_path.join(format!("{}.log", request_id))
    }

    /// Appends a log line to the request's temporary file.
    pub fn append(&self, request_id: &str, message: &str) {
        let path = self.get_log_path(request_id);
        // Open for append, create if not exists
        if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(path) {
            let _ = writeln!(file, "{}", message);
        }
    }

    /// Reads the current logs as a vector of strings (for snapshotting).
    pub fn read_current_logs(&self, request_id: &str) -> Result<Vec<String>> {
        let path = self.get_log_path(request_id);
        if !path.exists() {
            return Ok(Vec::new());
        }

        let file = File::open(&path)?;
        let reader = BufReader::new(file);
        let lines: Result<Vec<String>, _> = reader.lines().collect();
        Ok(lines?)
    }

    /// Reads the full log content and deletes the temporary file.
    pub fn finalize(&self, request_id: &str) -> Result<String> {
        let path = self.get_log_path(request_id);
        if !path.exists() {
            return Ok(String::new());
        }

        let mut file = File::open(&path)?;
        let mut content = String::new();
        file.read_to_string(&mut content)?;

        // Delete the file after reading
        let _ = fs::remove_file(path);

        Ok(content)
    }
    
    /// Cleans up all temporary files (e.g. on startup)
    pub fn cleanup_all(&self) {
        if let Ok(entries) = fs::read_dir(&self.root_path) {
            for entry in entries.flatten() {
                if let Ok(file_type) = entry.file_type() {
                    if file_type.is_file() {
                        let _ = fs::remove_file(entry.path());
                    }
                }
            }
        }
    }
}

/// Represents a log entry to be broadcasted.
#[derive(Clone, Debug)]
pub struct LogEntry {
    pub request_id: String,
    pub level: String,
    pub message: String,
    pub timestamp: i64,
}

/// A Tracing Layer that intercepts logs and writes them to the LogBufferManager
/// if a `request_id` field is present in the span or event.
pub struct FileRequestLogLayer {
    manager: Arc<LogBufferManager>,
    tx: broadcast::Sender<LogEntry>,
}

impl FileRequestLogLayer {
    pub fn new(manager: Arc<LogBufferManager>, tx: broadcast::Sender<LogEntry>) -> Self {
        Self { manager, tx }
    }
}

impl<S> Layer<S> for FileRequestLogLayer
where
    S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
{
    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
        let mut request_id = None;

        // 1. Try to find request_id in the event fields
        let mut visitor = RequestIdVisitor(&mut request_id);
        event.record(&mut visitor);

        // 2. If not in event, look in the current span's extensions
        if request_id.is_none() {
            if let Some(span) = ctx.lookup_current() {
                let extensions = span.extensions();
                if let Some(req_id) = extensions.get::<RequestId>() {
                    request_id = Some(req_id.0.clone());
                } else {
                    // Fallback: Iterate fields of the span (Less efficient, usually Extensions is better if we set it)
                    // But tracing doesn't auto-propagate fields to extensions unless we do it manually.
                    // A common pattern is to use a visitor on the Span during `on_new_span`.
                    // For simplicity here, we rely on `on_new_span` to extract and store it in Extensions.
                }
            }
        }

        if let Some(req_id) = request_id {
            // Format the message
            let now = Utc::now();
            let timestamp_str = now.to_rfc3339();
            let timestamp_millis = now.timestamp_millis();
            let level_str = event.metadata().level().to_string();
            
            let mut msg_visitor = MessageVisitor(String::new());
            event.record(&mut msg_visitor);
            let message = msg_visitor.0;

            let log_line = format!("[{}] [{}] {}", timestamp_str, level_str, message);
            self.manager.append(&req_id, &log_line);

            // Broadcast for realtime
            let entry = LogEntry {
                request_id: req_id,
                level: level_str,
                message,
                timestamp: timestamp_millis,
            };
            let _ = self.tx.send(entry);
        }
    }

    fn on_new_span(&self, attrs: &tracing::span::Attributes<'_>, id: &tracing::Id, ctx: Context<'_, S>) {
        // Extract request_id from span attributes and store in Extensions for easy access in on_event
        let mut request_id = None;
        let mut visitor = RequestIdVisitor(&mut request_id);
        attrs.record(&mut visitor);

        if let Some(req_id) = request_id {
            if let Some(span) = ctx.span(id) {
                span.extensions_mut().insert(RequestId(req_id));
            }
        }
    }
}

// --- Helper Structs for Tracing Visitors ---

#[derive(Clone)]
struct RequestId(String);

struct RequestIdVisitor<'a>(&'a mut Option<String>);

impl<'a> tracing::field::Visit for RequestIdVisitor<'a> {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        if field.name() == "request_id" {
            *self.0 = Some(format!("{:?}", value).replace('"', ""));
        }
    }

    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
        if field.name() == "request_id" {
            *self.0 = Some(value.to_string());
        }
    }
}

struct MessageVisitor(String);

impl tracing::field::Visit for MessageVisitor {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        if field.name() == "message" {
            self.0 = format!("{:?}", value);
        }
    }
    
    // Needed for standard fmt::Display implementation which tracing uses for `message` usually
    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
         if field.name() == "message" {
            self.0 = value.to_string();
        }
    }
}

