use core::{
    ffi::c_char,
    ptr::{null_mut, NonNull},
};

use chrono::{Datelike, TimeZone, Timelike, Utc};

use crate::{
    headers::{locale::locale_t, sys_types::time_t},
    util::CStringWriter,
};

use super::{get_timezone, tm, CDateTime};

unsafe fn fmt_time<Tz: TimeZone, T: CDateTime>(
    t: &T,
    tz: Tz,
    buffer: *mut c_char,
) -> Option<NonNull<c_char>> {
    const MONTHS: &[&str] = &[
        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    ];
    const WDAYS: &[&str] = &["Sun", "Mon", "Wed", "Thu", "Fri", "Sat"];

    use core::fmt::Write;

    let dt = t.to_datetime(tz)?;
    let mut writer = CStringWriter::from_ptr(buffer, usize::MAX)?;

    writeln!(
        writer,
        "{} {} {:02} {:02}:{:02}:{:02} {:04}",
        WDAYS[dt.weekday().num_days_from_sunday() as usize],
        MONTHS[dt.month0() as usize],
        dt.day(),
        dt.hour(),
        dt.minute(),
        dt.second(),
        dt.year()
    )
    .ok()?;

    writer.finish()
}

#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn asctime(timep: *const tm) -> *mut c_char {
    static mut BUFFER: [c_char; 32] = [0; 32];
    asctime_r(timep, BUFFER.as_mut_ptr())
}

#[no_mangle]
unsafe extern "C" fn asctime_r(timep: *const tm, buffer: *mut c_char) -> *mut c_char {
    let timep = timep.as_ref().unwrap();

    // Utc, already accounts for timezone
    match fmt_time(timep, Utc, buffer) {
        Some(ptr) => ptr.as_ptr(),
        None => null_mut(),
    }
}

#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn ctime(tp: *const time_t) -> *mut c_char {
    static mut BUFFER: [c_char; 32] = [0; 32];
    ctime_r(tp, BUFFER.as_mut_ptr())
}

#[no_mangle]
unsafe extern "C" fn ctime_r(tp: *const time_t, buffer: *mut c_char) -> *mut c_char {
    let tp = tp.as_ref().unwrap();
    let tz = get_timezone();

    match fmt_time(tp, tz, buffer) {
        Some(ptr) => ptr.as_ptr(),
        None => null_mut(),
    }
}

#[no_mangle]
unsafe extern "C" fn getdate(_string: *const c_char) -> *mut tm {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn getdate_r(_string: *const c_char, _timep: *mut tm) -> *mut tm {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn strftime(
    _dst: *mut c_char,
    _len: usize,
    _format: *const c_char,
    _timep: *const tm,
) -> usize {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn strftime_l(
    _dst: *mut c_char,
    _len: usize,
    _format: *const c_char,
    _timep: *const tm,
    _locale: locale_t,
) -> usize {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn strptime(
    _src: *const c_char,
    _format: *const c_char,
    _timep: *mut tm,
) -> *mut c_char {
    todo!()
}
