use crate::filters::IssueFilter;
use crate::filters::IssueLabelFilter;
use crate::filters::ProjectFilter;
use crate::filters::TeamFilter;
use crate::filters::UserFilter;
use crate::filters::WorkflowStateFilter;
use crate::types::Issue;
use crate::types::IssueCommentConnection;
use crate::types::IssueConnection;
use crate::types::IssueLabelConnection;
use crate::types::IssueSearchPayload;
use crate::types::ProjectConnection;
use crate::types::TeamConnection;
use crate::types::UserConnection;
use crate::types::WorkflowStateConnection;
use linear_schema::linear as schema;

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct IssuesArguments {
    pub first: Option<i32>,
    pub after: Option<String>,
    pub filter: Option<IssueFilter>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "IssuesArguments"
)]
pub struct IssuesQuery {
    #[arguments(first: $first, after: $after, filter: $filter)]
    pub issues: IssueConnection,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct IssueByIdArguments {
    pub id: String,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "IssueByIdArguments"
)]
pub struct IssueByIdQuery {
    #[arguments(id: $id)]
    pub issue: Option<Issue>,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct SearchIssuesArguments {
    pub term: String,
    #[cynic(rename = "includeComments")]
    pub include_comments: Option<bool>,
    pub first: Option<i32>,
    pub after: Option<String>,
    pub filter: Option<IssueFilter>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "SearchIssuesArguments"
)]
pub struct SearchIssuesQuery {
    #[arguments(term: $term, includeComments: $include_comments, first: $first, after: $after, filter: $filter)]
    #[cynic(rename = "searchIssues")]
    pub search_issues: IssueSearchPayload,
}

// ============================================================================
// Metadata queries
// ============================================================================

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct UsersArguments {
    pub first: Option<i32>,
    pub after: Option<String>,
    pub filter: Option<UserFilter>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "UsersArguments"
)]
pub struct UsersQuery {
    #[arguments(first: $first, after: $after, filter: $filter)]
    pub users: UserConnection,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct TeamsArguments {
    pub first: Option<i32>,
    pub after: Option<String>,
    pub filter: Option<TeamFilter>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "TeamsArguments"
)]
pub struct TeamsQuery {
    #[arguments(first: $first, after: $after, filter: $filter)]
    pub teams: TeamConnection,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct ProjectsArguments {
    pub first: Option<i32>,
    pub after: Option<String>,
    pub filter: Option<ProjectFilter>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "ProjectsArguments"
)]
pub struct ProjectsQuery {
    #[arguments(first: $first, after: $after, filter: $filter)]
    pub projects: ProjectConnection,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct WorkflowStatesArguments {
    pub first: Option<i32>,
    pub after: Option<String>,
    pub filter: Option<WorkflowStateFilter>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "WorkflowStatesArguments"
)]
pub struct WorkflowStatesQuery {
    #[arguments(first: $first, after: $after, filter: $filter)]
    #[cynic(rename = "workflowStates")]
    pub workflow_states: WorkflowStateConnection,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct IssueLabelsArguments {
    pub first: Option<i32>,
    pub after: Option<String>,
    pub filter: Option<IssueLabelFilter>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "IssueLabelsArguments"
)]
pub struct IssueLabelsQuery {
    #[arguments(first: $first, after: $after, filter: $filter)]
    #[cynic(rename = "issueLabels")]
    pub issue_labels: IssueLabelConnection,
}

// ============================================================================
// Issue relation queries
// ============================================================================

#[derive(cynic::QueryFragment, Debug, Clone)]
#[cynic(schema = "linear")]
pub struct IssueRelation {
    pub id: cynic::Id,
    #[cynic(rename = "relatedIssue")]
    pub related_issue: RelatedIssue,
}

#[derive(cynic::QueryFragment, Debug, Clone)]
#[cynic(schema = "linear", graphql_type = "Issue")]
pub struct RelatedIssue {
    pub id: cynic::Id,
}

#[derive(cynic::QueryFragment, Debug, Clone)]
#[cynic(schema = "linear")]
pub struct IssueRelationConnection {
    pub nodes: Vec<IssueRelation>,
}

#[derive(cynic::QueryFragment, Debug, Clone)]
#[cynic(schema = "linear", graphql_type = "Issue")]
pub struct IssueWithRelations {
    pub id: cynic::Id,
    pub relations: IssueRelationConnection,
    #[cynic(rename = "inverseRelations")]
    pub inverse_relations: IssueRelationConnection,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct IssueRelationsArguments {
    pub id: String,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "IssueRelationsArguments"
)]
pub struct IssueRelationsQuery {
    #[arguments(id: $id)]
    pub issue: Option<IssueWithRelations>,
}

// ============================================================================
// Issue comments query
// ============================================================================

#[derive(cynic::QueryFragment, Debug, Clone)]
#[cynic(
    schema = "linear",
    graphql_type = "Issue",
    variables = "IssueCommentsArguments"
)]
pub struct IssueWithComments {
    pub id: cynic::Id,
    pub identifier: String,
    #[arguments(first: $first, after: $after)]
    pub comments: IssueCommentConnection,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct IssueCommentsArguments {
    pub id: String,
    pub first: Option<i32>,
    pub after: Option<String>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
    graphql_type = "Query",
    schema = "linear",
    variables = "IssueCommentsArguments"
)]
pub struct IssueCommentsQuery {
    #[arguments(id: $id)]
    pub issue: Option<IssueWithComments>,
}
