use std::{cell::RefCell, fmt::Write};

use tracing::{
    field::{Field, Visit},
    Event, Subscriber,
};
use tracing_subscriber::{
    fmt::{format::Writer, FmtContext, FormatEvent, FormatFields},
    registry::LookupSpan,
};

use crate::telemetry::logging::request_id::REQUEST_IDENTIFIERS;
use crate::telemetry::logging::summary::REQUEST_SUMMARY;
use crate::telemetry::logging::targets;

#[inline]
fn write_json_body<W: std::fmt::Write>(w: &mut W, s: &str) -> std::fmt::Result {
    let bytes = s.as_bytes();
    let mut start = 0;
    for (i, &b) in bytes.iter().enumerate() {
        if b >= 0x20 && b != b'"' && b != b'\\' {
            continue;
        }
        if start < i {
            w.write_str(&s[start..i])?;
        }
        match b {
            b'"' => w.write_str("\\\"")?,
            b'\\' => w.write_str("\\\\")?,
            b'\n' => w.write_str("\\n")?,
            b'\r' => w.write_str("\\r")?,
            b'\t' => w.write_str("\\t")?,
            _ => write!(w, "\\u{b:04x}")?,
        }
        start = i + 1;
    }
    if start < bytes.len() {
        w.write_str(&s[start..])?;
    }

    Ok(())
}

#[inline]
fn write_json_str<W: std::fmt::Write>(w: &mut W, s: &str) -> std::fmt::Result {
    w.write_char('"')?;
    write_json_body(w, s)?;
    w.write_char('"')
}

struct JsonEscape<'a, W: std::fmt::Write>(&'a mut W);

impl<W: std::fmt::Write> std::fmt::Write for JsonEscape<'_, W> {
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        write_json_body(self.0, s)
    }
}

struct JsonVisitor<'a> {
    buf: &'a mut String,
}

impl JsonVisitor<'_> {
    fn key(&mut self, name: &str) {
        self.buf.push(',');
        let _ = write_json_str(&mut *self.buf, name);
        self.buf.push(':');
    }
}

impl Visit for JsonVisitor<'_> {
    fn record_str(&mut self, f: &Field, v: &str) {
        self.key(f.name());
        let _ = write_json_str(&mut *self.buf, v);
    }
    fn record_i64(&mut self, f: &Field, v: i64) {
        self.key(f.name());
        let _ = write!(self.buf, "{v}");
    }
    fn record_u64(&mut self, f: &Field, v: u64) {
        self.key(f.name());
        let _ = write!(self.buf, "{v}");
    }
    fn record_f64(&mut self, f: &Field, v: f64) {
        self.key(f.name());
        if v.is_finite() {
            let _ = write!(self.buf, "{v}");
        } else {
            let _ = write!(self.buf, "\"{v}\"");
        }
    }
    fn record_bool(&mut self, f: &Field, v: bool) {
        self.key(f.name());
        self.buf.push_str(if v { "true" } else { "false" });
    }
    fn record_error(&mut self, f: &Field, v: &(dyn std::error::Error + 'static)) {
        self.key(f.name());
        self.buf.push('"');
        let _ = write!(JsonEscape(&mut *self.buf), "{v}");
        self.buf.push('"');
    }
    fn record_debug(&mut self, f: &Field, v: &dyn std::fmt::Debug) {
        self.key(f.name());
        self.buf.push('"');
        let _ = write!(JsonEscape(&mut *self.buf), "{v:?}");
        self.buf.push('"');
    }
}

pub struct RouterJsonFormat;

impl<S, N> FormatEvent<S, N> for RouterJsonFormat
where
    S: Subscriber + for<'a> LookupSpan<'a>,
    N: for<'a> FormatFields<'a> + 'static,
{
    fn format_event(
        &self,
        _ctx: &FmtContext<'_, S, N>,
        mut writer: Writer<'_>,
        event: &Event<'_>,
    ) -> std::fmt::Result {
        thread_local! {
            static BUF: RefCell<String> = const { RefCell::new(String::new()) };
        }

        let meta = event.metadata();

        BUF.with(|cell| {
            let mut buf = cell.borrow_mut();
            buf.clear();

            buf.push_str("{\"timestamp\":\"");
            let _ = write!(
                buf,
                "{}",
                chrono::Utc::now().format("%Y-%m-%dT%H:%M:%S%.6fZ")
            );
            buf.push_str("\",\"level\":\"");
            buf.push_str(meta.level().as_str());
            buf.push_str("\",\"target\":");
            write_json_str(&mut *buf, meta.target())?;

            let _ = REQUEST_IDENTIFIERS.try_with(|ids| -> std::fmt::Result {
                buf.push_str(",\"request_id\":");
                write_json_str(&mut *buf, ids.req_id())?;
                if let Some(trace_id) = ids.trace_id() {
                    buf.push_str(",\"trace_id\":");
                    write_json_str(&mut *buf, trace_id)?;
                }
                if let Ok(correlations) = ids.correlations.lock() {
                    for (key, value) in correlations.iter() {
                        buf.push(',');
                        write_json_str(&mut *buf, key)?;
                        buf.push(':');
                        write_json_str(&mut *buf, value)?;
                    }
                }
                Ok(())
            });

            event.record(&mut JsonVisitor { buf: &mut buf });

            if meta.target() == targets::SUMMARY {
                let _ = REQUEST_SUMMARY.try_with(|summary| -> std::fmt::Result {
                    if let Ok(custom) = summary.custom.lock() {
                        for (key, value) in custom.iter() {
                            buf.push(',');
                            write_json_str(&mut *buf, key)?;
                            buf.push(':');
                            let _ = write!(buf, "{value}");
                        }
                    }
                    Ok(())
                });
            }

            buf.push_str("}\n");
            writer.write_str(&buf)
        })
    }
}
