use model::{
    issues::{LabelModel, MilestoneModel},
    pull_request::PullRequestModel,
    repos::RepoModel,
    users::UserModel,
};
use serde::{Deserialize, Serialize};

use crate::non_empty;

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct IssueResponse {
    pub number: i64,
    pub title: String,
    pub body: Option<String>,
    pub state: String,
    pub priority: String,
    #[schema(value_type = Option<String>)]
    pub due_at: Option<chrono::DateTime<chrono::Utc>>,
    pub author: IssueAuthor,
    pub closed_by: Option<IssueAuthor>,
    #[schema(value_type = Option<String>)]
    pub closed_at: Option<chrono::DateTime<chrono::Utc>>,
    #[schema(value_type = String)]
    pub created_at: chrono::DateTime<chrono::Utc>,
    #[schema(value_type = String)]
    pub updated_at: chrono::DateTime<chrono::Utc>,
    pub labels: Vec<LabelResponse>,
    pub assignees: Vec<IssueAuthor>,
    pub milestone: Option<MilestoneResponse>,
    pub repos: Vec<IssueRepoResponse>,
    pub pull_requests: Vec<IssuePullRequestResponse>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct IssueAuthor {
    pub username: String,
    pub display_name: Option<String>,
    pub avatar_url: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct LabelResponse {
    #[schema(value_type = String)]
    pub id: uuid::Uuid,
    pub name: String,
    pub color: String,
    pub description: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct MilestoneResponse {
    #[schema(value_type = String)]
    pub id: uuid::Uuid,
    pub title: String,
    pub description: Option<String>,
    pub state: String,
    #[schema(value_type = Option<String>)]
    pub due_at: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct IssueRepoResponse {
    #[schema(value_type = String)]
    pub id: uuid::Uuid,
    pub name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct IssuePullRequestResponse {
    #[schema(value_type = String)]
    pub id: uuid::Uuid,
    pub number: i64,
    pub title: String,
    pub state: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct IssueCommentResponse {
    #[schema(value_type = String)]
    pub id: uuid::Uuid,
    pub author: IssueAuthor,
    pub body: String,
    #[schema(value_type = String)]
    pub created_at: chrono::DateTime<chrono::Utc>,
    #[schema(value_type = String)]
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct IssueEventResponse {
    pub actor: Option<IssueAuthor>,
    pub event: String,
    pub from_value: Option<String>,
    pub to_value: Option<String>,
    #[schema(value_type = String)]
    pub created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct IssueReactionResponse {
    #[schema(value_type = String)]
    pub id: uuid::Uuid,
    pub user: IssueAuthor,
    pub reaction: String,
    #[schema(value_type = String)]
    pub created_at: chrono::DateTime<chrono::Utc>,
}

pub fn issue_author(user: UserModel) -> IssueAuthor {
    IssueAuthor {
        username: user.username,
        display_name: non_empty(user.display_name),
        avatar_url: non_empty(user.avatar_url),
    }
}

pub fn label_response(label: LabelModel) -> LabelResponse {
    LabelResponse {
        id: label.id,
        name: label.name,
        color: label.color,
        description: non_empty(label.description.unwrap_or_default()),
    }
}

pub fn milestone_response(milestone: MilestoneModel) -> MilestoneResponse {
    MilestoneResponse {
        id: milestone.id,
        title: milestone.title,
        description: non_empty(milestone.description.unwrap_or_default()),
        state: milestone.state,
        due_at: milestone.due_at,
    }
}

pub fn issue_repo_response(repo: RepoModel) -> IssueRepoResponse {
    IssueRepoResponse {
        id: repo.id,
        name: repo.name,
    }
}

pub fn issue_pr_response(pr: PullRequestModel) -> IssuePullRequestResponse {
    IssuePullRequestResponse {
        id: pr.id,
        number: pr.number,
        title: pr.title,
        state: pr.state,
    }
}

#[derive(Debug, Clone, Deserialize, utoipa::IntoParams)]
pub struct IssueFilter {
    pub state: Option<String>,
    pub label: Option<String>,
    pub milestone: Option<String>,
    pub assignee: Option<String>,
    pub priority: Option<String>,
}
