use super::{helpers::*, *};

pub fn home_page(server_name: String, home: HomeData) -> HomePage {
    let (heat_cells, contribution_count) = heat_cells(&home.heatmap);
    HomePage {
        server_name,
        activities: activity_rows(&home.activities),
        heat_cells,
        contribution_count,
        next_page: home.next_page.map(|page| page as u32),
    }
}

pub fn activity_rows(activities: &[models::Activity]) -> Vec<ActivityRow> {
    activities.iter().map(activity_row).collect()
}

pub fn server_activity_page(page: gotcha_gitea::Page<models::Activity>) -> ServerActivityPage {
    ServerActivityPage {
        rows: page
            .items
            .iter()
            .map(|activity| {
                let mut row = activity_row(activity);
                let actor = activity
                    .act_user
                    .as_ref()
                    .and_then(|user| user.login.as_deref())
                    .unwrap_or("unknown user");
                row.title = format!("@{actor} · {}", row.title);
                row
            })
            .collect(),
        has_more: page.has_more,
    }
}

fn activity_row(activity: &models::Activity) -> ActivityRow {
    use models::activity::OpType;

    let repository = activity
        .repo
        .as_ref()
        .and_then(|repo| repo.full_name.as_deref())
        .unwrap_or("repository");
    let branch = activity
        .ref_name
        .as_deref()
        .and_then(|name| name.strip_prefix("refs/heads/").or(Some(name)))
        .unwrap_or("default branch");
    let (icon, title) = match activity.op_type {
        Some(OpType::CommitRepo) if activity.content.as_deref().unwrap_or("").is_empty() => (
            ActivityIcon::Branch,
            format!("Created branch {branch} in {repository}"),
        ),
        Some(OpType::CommitRepo | OpType::MirrorSyncPush) => (
            ActivityIcon::Push,
            format!("Pushed to {branch} in {repository}"),
        ),
        Some(OpType::CreateRepo) => (ActivityIcon::Repository, format!("Created {repository}")),
        Some(OpType::RenameRepo) => (ActivityIcon::Repository, format!("Renamed {repository}")),
        Some(OpType::StarRepo) => (ActivityIcon::Repository, format!("Starred {repository}")),
        Some(OpType::WatchRepo) => (
            ActivityIcon::Repository,
            format!("Started watching {repository}"),
        ),
        Some(OpType::CreateIssue) => (
            ActivityIcon::Issue,
            format!("Opened an issue in {repository}"),
        ),
        Some(OpType::CloseIssue) => (
            ActivityIcon::Issue,
            format!("Closed an issue in {repository}"),
        ),
        Some(OpType::ReopenIssue) => (
            ActivityIcon::Issue,
            format!("Reopened an issue in {repository}"),
        ),
        Some(OpType::CommentIssue) => (
            ActivityIcon::Issue,
            format!("Commented on an issue in {repository}"),
        ),
        Some(OpType::CreatePullRequest) => (
            ActivityIcon::PullRequest,
            format!("Opened a pull request in {repository}"),
        ),
        Some(OpType::MergePullRequest | OpType::AutoMergePullRequest) => (
            ActivityIcon::PullRequest,
            format!("Merged a pull request in {repository}"),
        ),
        Some(OpType::ClosePullRequest) => (
            ActivityIcon::PullRequest,
            format!("Closed a pull request in {repository}"),
        ),
        Some(OpType::ReopenPullRequest) => (
            ActivityIcon::PullRequest,
            format!("Reopened a pull request in {repository}"),
        ),
        Some(OpType::CommentPull) => (
            ActivityIcon::PullRequest,
            format!("Commented on a pull request in {repository}"),
        ),
        Some(OpType::ApprovePullRequest) => (
            ActivityIcon::PullRequest,
            format!("Approved a pull request in {repository}"),
        ),
        Some(OpType::RejectPullRequest) => (
            ActivityIcon::PullRequest,
            format!("Requested changes in {repository}"),
        ),
        Some(OpType::PushTag) => (
            ActivityIcon::Tag,
            format!("Pushed tag {branch} in {repository}"),
        ),
        Some(OpType::DeleteTag) => (
            ActivityIcon::Tag,
            format!("Deleted tag {branch} in {repository}"),
        ),
        Some(OpType::DeleteBranch) => (
            ActivityIcon::Branch,
            format!("Deleted branch {branch} in {repository}"),
        ),
        Some(OpType::PublishRelease) => (
            ActivityIcon::Release,
            format!("Published a release in {repository}"),
        ),
        Some(_) | None => (ActivityIcon::Repository, format!("Updated {repository}")),
    };
    let target = activity::target(activity);
    let (target, owner, repository, number, sha) = match target {
        Some(activity::Target::Repository { owner, repository }) => (
            ActivityTargetKind::Repository,
            owner,
            repository,
            0,
            String::new(),
        ),
        Some(activity::Target::Issue {
            owner,
            repository,
            number,
        }) => (
            ActivityTargetKind::Issue,
            owner,
            repository,
            number,
            String::new(),
        ),
        Some(activity::Target::Pull {
            owner,
            repository,
            number,
        }) => (
            ActivityTargetKind::PullRequest,
            owner,
            repository,
            number,
            String::new(),
        ),
        Some(activity::Target::Commit {
            owner,
            repository,
            sha,
        }) => (ActivityTargetKind::Commit, owner, repository, 0, sha),
        None => (
            ActivityTargetKind::None,
            String::new(),
            String::new(),
            0,
            String::new(),
        ),
    };
    ActivityRow {
        icon,
        title,
        detail: activity_detail(activity),
        meta: compact_date(activity.created.as_deref()),
        target,
        owner,
        repository,
        number,
        sha,
    }
}

fn activity_detail(activity: &models::Activity) -> String {
    let comment = activity
        .comment
        .as_ref()
        .and_then(|comment| comment.body.as_deref());
    let text = comment.or(activity.content.as_deref()).unwrap_or("");
    if comment.is_none()
        && let Some(payload) = activity::commit_activity(activity)
    {
        let message = payload
            .commits
            .last()
            .map(|commit| summary(&commit.message))
            .unwrap_or_default();
        if !message.is_empty() {
            return match payload.count {
                1 => format!("1 commit · {message}"),
                count => format!("{count} commits · {message}"),
            };
        }
    }
    let text = summary(text);
    if text.is_empty() {
        "Server activity".into()
    } else {
        text
    }
}

pub(super) fn heat_cells(data: &[models::UserHeatmapData]) -> (Vec<HeatCell>, i64) {
    let latest = data
        .iter()
        .filter_map(|entry| entry.timestamp)
        .max()
        .unwrap_or(0)
        / 86_400;
    let (year, month, _) = civil_from_days(latest);
    let first_month = year * 12 + month - 1 - 8;
    let start = days_from_civil(
        first_month.div_euclid(12),
        first_month.rem_euclid(12) + 1,
        1,
    );
    let mut counts = vec![0_i64; (latest - start + 1) as usize];
    for entry in data {
        let day = entry.timestamp.unwrap_or_default() / 86_400;
        if (start..=latest).contains(&day) {
            counts[(day - start) as usize] += entry.contributions.unwrap_or_default();
        }
    }
    let maximum = counts.iter().copied().max().unwrap_or_default();
    let contribution_count = counts.iter().sum();
    let cells = counts
        .into_iter()
        .enumerate()
        .map(|(index, count)| HeatCell {
            level: if count == 0 || maximum == 0 {
                0
            } else {
                ((count * 4 + maximum - 1) / maximum).clamp(1, 4) as u32
            },
            timestamp: (start + index as i64) * 86_400,
        })
        .collect();
    (cells, contribution_count)
}
