use comfy_table::{Attribute, Cell, Color};
use std::io::IsTerminal;

use crate::model::{Effort, Priority, Status};

pub struct Theme {
    pub red: &'static str,
    pub green: &'static str,
    pub yellow: &'static str,
    pub blue: &'static str,
    pub cyan: &'static str,
    pub bold: &'static str,
    pub bold_red: &'static str,
    pub bold_yellow: &'static str,
    pub reset: &'static str,
}

const ON: Theme = Theme {
    red: "\x1b[31m",
    green: "\x1b[32m",
    yellow: "\x1b[33m",
    blue: "\x1b[34m",
    cyan: "\x1b[36m",
    bold: "\x1b[1m",
    bold_red: "\x1b[1;31m",
    bold_yellow: "\x1b[1;33m",
    reset: "\x1b[0m",
};

const OFF: Theme = Theme {
    red: "",
    green: "",
    yellow: "",
    blue: "",
    cyan: "",
    bold: "",
    bold_red: "",
    bold_yellow: "",
    reset: "",
};

fn use_color(is_tty: bool) -> bool {
    std::env::var_os("NO_COLOR").is_none() && is_tty
}

/// Colour theme for stdout.
pub fn stdout_theme() -> &'static Theme {
    if use_color(std::io::stdout().is_terminal()) {
        &ON
    } else {
        &OFF
    }
}

/// Colour theme for stderr.
pub fn stderr_theme() -> &'static Theme {
    if use_color(std::io::stderr().is_terminal()) {
        &ON
    } else {
        &OFF
    }
}

/// Whether stdout should use colour.
pub fn stdout_use_color() -> bool {
    use_color(std::io::stdout().is_terminal())
}

/// A table cell with bold text.
pub fn cell_bold(text: impl ToString, use_color: bool) -> Cell {
    let cell = Cell::new(text);
    if use_color {
        cell.add_attribute(Attribute::Bold)
    } else {
        cell
    }
}

/// A table cell with a coloured foreground.
pub fn cell_fg(text: impl ToString, color: Color, use_color: bool) -> Cell {
    let cell = Cell::new(text);
    if use_color {
        cell.fg(color)
    } else {
        cell
    }
}

impl Theme {
    /// ANSI escape for a task status.
    pub fn status(&self, s: Status) -> &str {
        match s {
            Status::Open => self.green,
            Status::InProgress => self.bold_yellow,
            Status::Closed => "",
        }
    }

    /// ANSI escape for a priority level.
    pub fn priority(&self, p: Priority) -> &str {
        match p {
            Priority::High => self.bold_red,
            Priority::Medium => "",
            Priority::Low => self.cyan,
        }
    }

    /// ANSI escape for an effort level.
    pub fn effort(&self, e: Effort) -> &str {
        match e {
            Effort::High => self.bold_red,
            Effort::Medium => "",
            Effort::Low => self.cyan,
        }
    }
}

/// A table cell styled for a task status.
pub fn cell_status(text: impl ToString, s: Status, use_color: bool) -> Cell {
    let cell = Cell::new(text);
    if !use_color {
        return cell;
    }
    match s {
        Status::Open => cell.fg(Color::Green),
        Status::InProgress => cell.fg(Color::Yellow).add_attribute(Attribute::Bold),
        Status::Closed => cell,
    }
}

/// A table cell styled for a priority level.
pub fn cell_priority(text: impl ToString, p: Priority, use_color: bool) -> Cell {
    let cell = Cell::new(text);
    if !use_color {
        return cell;
    }
    match p {
        Priority::High => cell.fg(Color::Red).add_attribute(Attribute::Bold),
        Priority::Medium => cell,
        Priority::Low => cell.fg(Color::Cyan),
    }
}

/// A table cell styled for an effort level.
pub fn cell_effort(text: impl ToString, e: Effort, use_color: bool) -> Cell {
    let cell = Cell::new(text);
    if !use_color {
        return cell;
    }
    match e {
        Effort::High => cell.fg(Color::Red).add_attribute(Attribute::Bold),
        Effort::Medium => cell,
        Effort::Low => cell.fg(Color::Cyan),
    }
}
