use std::{borrow::Cow, sync::LazyLock};

use console::strip_ansi_codes;
use owo_colors::{OwoColorize, Style};

pub const DOWNLOAD_STYLE: Style = Style::new().blue().bold();
pub const EXTRACT_STYLE: Style = Style::new().green().bold();
pub const NAME_WIDTH_CUT: usize = 4;
pub const NAME_WIDTH_MIN: usize = 5;
pub const INFO_WIDTH: usize = 12;
pub static DOWNLOAD_CHAR: LazyLock<String> = LazyLock::new(|| "-".blue().bold().to_string());
pub static DONE_CHAR: LazyLock<String> = LazyLock::new(|| "=".green().bold().to_string());
pub static INPROGRESS_CHAR: LazyLock<String> = LazyLock::new(|| "-".purple().bold().to_string());

pub trait StrExt<'a>: Into<Cow<'a, str>> {
    fn pad_string(self, width: usize) -> String {
        let s = self.into();
        let len = s.no_ansi_len();
        if len == width {
            s.to_string()
        } else if len > width {
            s.chars().take(width.saturating_sub(2)).collect::<String>() + ".."
        } else {
            let mut padded = s.to_string();
            padded.push_str(&" ".repeat(width - len));
            padded
        }
    }
}

pub trait AStrExt {
    fn no_ansi_len(&self) -> usize;
}
impl AStrExt for String {
    fn no_ansi_len(&self) -> usize {
        let s = strip_ansi_codes(self);
        s.len()
    }
}
impl AStrExt for &str {
    fn no_ansi_len(&self) -> usize {
        let s = strip_ansi_codes(self);
        s.len()
    }
}
impl AStrExt for str {
    fn no_ansi_len(&self) -> usize {
        let s = strip_ansi_codes(self);
        s.len()
    }
}
impl<'a, T: Into<Cow<'a, str>>> StrExt<'a> for T {}

pub fn pad_string<'a, S: Into<Cow<'a, str>>>(s: S, width: usize) -> String {
    let s = s.into();
    let trimmed = strip_ansi_codes(&s);
    let len = trimmed.len();
    if len == width {
        s.to_string()
    } else if len > width {
        s.chars().take(width.saturating_sub(2)).collect::<String>() + ".."
    } else {
        let mut padded = s.to_string();
        padded.push_str(&" ".repeat(width - len));
        padded
    }
}
