use gotcha_gitea::models::{
    ChangedFile, Comment, Commit, Issue, Milestone, PullRequest, PullReview,
};
use gotcha_gitea::pull_state;

use crate::print_table;

pub(super) fn print_issues(issues: &[Issue]) {
    print_table(
        &[
            ("INDEX", 8),
            ("STATE", 10),
            ("TITLE", 60),
            ("MILESTONE", 30),
            ("LABELS", 30),
        ],
        issues
            .iter()
            .map(|issue| {
                vec![
                    issue.number.unwrap_or_default().to_string(),
                    issue.state.as_deref().unwrap_or("unknown").into(),
                    one_line(issue.title.as_deref().unwrap_or("")).into(),
                    issue
                        .milestone
                        .as_deref()
                        .and_then(|milestone| milestone.title.as_deref())
                        .unwrap_or("-")
                        .into(),
                    issue_labels(issue),
                ]
            })
            .collect(),
    );
}

pub(super) fn print_issue(issue: &Issue) {
    println!(
        "#{} [{}] {}",
        issue.number.unwrap_or_default(),
        issue.state.as_deref().unwrap_or("unknown"),
        issue.title.as_deref().unwrap_or("")
    );
    field("url", issue.html_url.as_deref());
    field(
        "author",
        issue.user.as_deref().and_then(|user| user.login.as_deref()),
    );
    let milestone = issue
        .milestone
        .as_deref()
        .and_then(|milestone| milestone.title.as_deref());
    field("milestone", milestone);
    let labels = issue_labels(issue);
    field("labels", (!labels.is_empty()).then_some(labels.as_str()));
    let assignees = issue
        .assignees
        .as_deref()
        .unwrap_or_default()
        .iter()
        .filter_map(|user| user.login.as_deref())
        .collect::<Vec<_>>()
        .join(",");
    field(
        "assignees",
        (!assignees.is_empty()).then_some(assignees.as_str()),
    );
    field("due", issue.due_date.as_deref());
    field("created", issue.created_at.as_deref());
    field("updated", issue.updated_at.as_deref());
    println!("comments: {}", issue.comments.unwrap_or_default());
    body(issue.body.as_deref());
}

fn issue_labels(issue: &Issue) -> String {
    issue
        .labels
        .as_deref()
        .unwrap_or_default()
        .iter()
        .filter_map(|label| label.name.as_deref())
        .collect::<Vec<_>>()
        .join(",")
}

pub(super) fn print_comments(comments: &[Comment]) {
    for comment in comments {
        println!(
            "{} · {}",
            comment
                .user
                .as_deref()
                .and_then(|user| user.login.as_deref())
                .unwrap_or("unknown"),
            comment.created_at.as_deref().unwrap_or("-")
        );
        body(comment.body.as_deref());
    }
}

pub(super) fn print_milestones(milestones: &[Milestone]) {
    print_table(
        &[
            ("ID", 8),
            ("STATE", 10),
            ("TITLE", 60),
            ("DUE", 25),
            ("OPEN/CLOSED", 12),
        ],
        milestones
            .iter()
            .map(|milestone| {
                vec![
                    milestone.id.unwrap_or_default().to_string(),
                    milestone.state.as_deref().unwrap_or("unknown").into(),
                    one_line(milestone.title.as_deref().unwrap_or("")).into(),
                    milestone.due_on.as_deref().unwrap_or("-").into(),
                    format!(
                        "{}/{}",
                        milestone.open_issues.unwrap_or_default(),
                        milestone.closed_issues.unwrap_or_default()
                    ),
                ]
            })
            .collect(),
    );
}

pub(super) fn print_milestone(milestone: &Milestone) {
    println!(
        "{} [{}] {}",
        milestone.id.unwrap_or_default(),
        milestone.state.as_deref().unwrap_or("unknown"),
        milestone.title.as_deref().unwrap_or("")
    );
    field("due", milestone.due_on.as_deref());
    println!(
        "issues: {} open, {} closed",
        milestone.open_issues.unwrap_or_default(),
        milestone.closed_issues.unwrap_or_default()
    );
    body(milestone.description.as_deref());
}

pub(super) fn print_pulls(pulls: &[PullRequest]) {
    print_table(
        &[("INDEX", 8), ("STATE", 10), ("TITLE", 70), ("UPDATED", 25)],
        pulls
            .iter()
            .map(|pull| {
                vec![
                    pull.number.unwrap_or_default().to_string(),
                    pull.state.as_deref().unwrap_or("unknown").into(),
                    one_line(pull.title.as_deref().unwrap_or("")).into(),
                    pull.updated_at.as_deref().unwrap_or("-").into(),
                ]
            })
            .collect(),
    );
}

pub(super) fn print_pull(pull: &PullRequest) {
    println!(
        "#{} [{}] {}",
        pull.number.unwrap_or_default(),
        pull_state(pull),
        pull.title.as_deref().unwrap_or("")
    );
    field("url", pull.html_url.as_deref());
    field(
        "author",
        pull.user.as_deref().and_then(|user| user.login.as_deref()),
    );
    field("updated", pull.updated_at.as_deref());
    println!("mergeable: {}", pull.mergeable.unwrap_or(false));
    body(pull.body.as_deref());
}

pub(super) fn print_commits(commits: &[Commit]) {
    print_table(
        &[("SHA", 40), ("MESSAGE", 80)],
        commits
            .iter()
            .map(|commit| {
                vec![
                    commit.sha.as_deref().unwrap_or("unknown").into(),
                    one_line(
                        commit
                            .commit
                            .as_deref()
                            .and_then(|commit| commit.message.as_deref())
                            .unwrap_or(""),
                    )
                    .into(),
                ]
            })
            .collect(),
    );
}

pub(super) fn print_files(files: &[ChangedFile]) {
    print_table(
        &[("STATUS", 12), ("CHANGES", 10), ("FILE", 90)],
        files
            .iter()
            .map(|file| {
                vec![
                    file.status.as_deref().unwrap_or("unknown").into(),
                    file.changes.unwrap_or_default().to_string(),
                    file.filename.as_deref().unwrap_or("").into(),
                ]
            })
            .collect(),
    );
}

pub(super) fn print_reviews(reviews: &[PullReview]) {
    print_table(
        &[
            ("ID", 12),
            ("STATE", 20),
            ("REVIEWER", 25),
            ("SUBMITTED", 25),
        ],
        reviews
            .iter()
            .map(|review| {
                vec![
                    review.id.unwrap_or_default().to_string(),
                    review.state.as_deref().unwrap_or("unknown").into(),
                    review
                        .user
                        .as_deref()
                        .and_then(|user| user.login.as_deref())
                        .unwrap_or("unknown")
                        .into(),
                    review.submitted_at.as_deref().unwrap_or("-").into(),
                ]
            })
            .collect(),
    );
}

fn field(name: &str, value: Option<&str>) {
    if let Some(value) = value.filter(|value| !value.is_empty()) {
        println!("{name}: {value}");
    }
}

fn body(value: Option<&str>) {
    if let Some(value) = value.filter(|value| !value.is_empty()) {
        println!("\n{value}");
    }
}

fn one_line(value: &str) -> &str {
    value.lines().next().unwrap_or_default()
}
