import { gql } from 'graphql-modules';

export default gql`
  extend type Mutation {
    createSchemaProposal(input: CreateSchemaProposalInput!): CreateSchemaProposalResult!
    reviewSchemaProposal(input: ReviewSchemaProposalInput!): ReviewSchemaProposalResult!
    replyToSchemaProposalReview(
      input: ReplyToSchemaProposalReviewInput!
    ): ReplyToSchemaProposalReviewResult!
  }

  type ReplyToSchemaProposalReviewResult {
    ok: ReplyToSchemaProposalReviewOk
    error: ReplyToSchemaProposalReviewError
  }

  type ReplyToSchemaProposalReviewOk {
    reply: SchemaProposalComment!
  }

  type ReplyToSchemaProposalReviewError implements Error {
    message: String!
  }

  type ReviewSchemaProposalResult {
    ok: ReviewSchemaProposalOk
    error: ReviewSchemaProposalError
  }

  type ReviewSchemaProposalOk {
    review: SchemaProposalReview!
  }

  type ReviewSchemaProposalError implements Error {
    message: String!
  }

  type CreateSchemaProposalResult {
    error: CreateSchemaProposalError
    ok: CreateSchemaProposalOk
  }

  type CreateSchemaProposalOk {
    schemaProposal: SchemaProposal!
  }

  type CreateSchemaProposalErrorDetails {
    """
    Error message for the input title.
    """
    title: String
    """
    Error message for the input description.
    """
    description: String
  }

  type CreateSchemaProposalError implements Error {
    message: String!
    details: CreateSchemaProposalErrorDetails!
  }

  input CreateSchemaProposalInput {
    """
    Reference to the proposal's target. Either an ID or path.
    """
    target: TargetReferenceInput!

    """
    The title of the proposal. A short description of the proposal's main focus/theme.
    """
    title: String!

    """
    Display name of the person who initially created this proposal
    """
    author: String!

    """
    If no description was provided then this will be an empty string.
    """
    description: String! = ""

    """
    The default initial stage is OPEN. Set this to true to create this as proposal
    as a DRAFT instead.
    """
    isDraft: Boolean! = false
  }

  input ReviewSchemaProposalInput {
    """
    The schema proposal that this review is being made on.
    """
    schemaProposalId: ID!

    """
    The schema coordinate being referenced. E.g. "Type.field".
    If null, then this review is for the entire proposal.
    """
    coordinate: String

    """
    One or both of stageTransition or initialComment inputs is/are required.
    """
    stageTransition: SchemaProposalStage

    """
    The initial comment message attached to the review
    """
    commentBody: String! = ""

    """
    The service this review applies to. If the target is a monorepo, then use
    an empty string.
    """
    serviceName: String!
  }

  input ReplyToSchemaProposalReviewInput {
    schemaProposalReviewId: ID!
    body: String!
  }

  extend type Subscription {
    schemaProposalComposition(
      input: SchemaProposalCompositionSubscriptionInput!
    ): SchemaProposalCompositionEvent!
  }

  type SchemaProposalCompositionEvent {
    status: ProposalCompositionStatus!
    timestamp: String!
  }

  input SchemaProposalCompositionSubscriptionInput {
    proposalId: ID!
  }

  extend type Query {
    schemaProposals(
      after: String
      first: Int! = 30
      input: SchemaProposalsInput!
    ): SchemaProposalConnection
    schemaProposal(input: SchemaProposalInput!): SchemaProposal
  }

  input SchemaProposalsInput {
    target: TargetReferenceInput!
    stages: [SchemaProposalStage!]
  }

  input SchemaProposalInput {
    """
    Unique identifier of the desired SchemaProposal
    """
    id: ID!
  }

  type SchemaProposalConnection {
    edges: [SchemaProposalEdge!]
    pageInfo: PageInfo!
  }

  type SchemaProposalEdge {
    cursor: String!
    node: SchemaProposal!
  }

  enum SchemaProposalStage {
    DRAFT
    OPEN
    APPROVED
    IMPLEMENTED
    CLOSED
  }

  type SchemaProposal {
    id: ID!
    createdAt: DateTime!

    """
    A paginated list of reviews.
    """
    reviews(after: String, first: Int! = 30): SchemaProposalReviewConnection

    """
    The current stage of the proposal. Proposals should be transitioned through the
    course of the review from DRAFT, OPEN, APPROVED, IMPLEMENTED, but may be CLOSED
    at any point in its lifecycle prior to being IMPLEMENTED. DRAFT, OPEN, APPROVED,
    and CLOSED may be triggered by user action. But IMPLEMENTED can only happen if
    the target schema contains the proposed changes while the proposal is in the
    APPROVED state.
    """
    stage: SchemaProposalStage!

    """
    A short title of this proposal. Meant to give others an easy way to refer to this
    set of changes.
    """
    title: String!

    """
    The proposal description. If no description was given, this will be an empty string.
    """
    description: String!

    """
    When the proposal was last modified. Adding a review or comment does not count.
    """
    updatedAt: DateTime!

    """
    The author of the proposal. If no author has been assigned, then this returns an empty string.
    The author is taken from the author of the oldest check ran for this proposal.
    """
    author: String!

    """
    The checks associated with this proposal. Each proposed change triggers a check
    for the set of changes. And each service is checked separately. This is a limitation
    of the schema check API at this time.

    The check "cursor" can be considered the proposal "version".
    """
    checks(
      after: String
      first: Int! = 20
      input: SchemaProposalChecksInput!
    ): SchemaCheckConnection

    """
    Applies changes to each service subgraph for each of the service's latest check belonging to the SchemaProposal.
    """
    rebasedSchemaSDL(
      """
      A schema check cursor. This indicates from where in the list of schema checks to start applying
      the changes. The check "cursor" can be considered the proposal "version".
      """
      after: String
      """
      The number of service SDLs return
      """
      first: Int! = 20
    ): SchemaConnection

    """
    Applies changes to the supergraph for each of the service's latest check belonging to the SchemaProposal.
    """
    rebasedSupergraphSDL(
      """
      A schema check cursor. This indicates from where in the list of schema checks to start applying
      the changes.
      """
      fromCursor: String
    ): String

    compositionStatus: ProposalCompositionStatus
    compositionTimestamp: DateTime
    compositionStatusReason: String
  }

  enum ProposalCompositionStatus {
    ERROR
    SUCCESS
  }

  input SchemaProposalChecksInput {
    """
    Set to "true" to only return the latest checks for each service.
    """
    latestPerService: Boolean! = false
  }

  type SchemaProposalReviewEdge {
    cursor: String!
    node: SchemaProposalReview!
  }

  type SchemaProposalReviewConnection {
    edges: [SchemaProposalReviewEdge!]
    pageInfo: PageInfo!
  }

  type SchemaProposalCommentEdge {
    cursor: String!
    node: SchemaProposalComment!
  }

  type SchemaProposalCommentConnection {
    edges: [SchemaProposalCommentEdge!]
    pageInfo: PageInfo!
  }

  type SchemaProposalReview {
    """
    A UUID unique to this review. Used for querying.
    """
    id: ID!

    """
    Comments attached to this review.
    """
    comments(after: String, first: Int! = 200): SchemaProposalCommentConnection

    """
    When the review was first made. Only a review's comments are mutable, so there is no
    updatedAt on the review.
    """
    createdAt: DateTime!

    """
    The text on the line at the time of being reviewed. This should be displayed if that
    coordinate's text has been modified

    If the "lineText" is null then this review references the entire SchemaProposalVersion
    and not any specific line within the proposal.
    """
    lineText: String

    """
    The coordinate being referenced by this review. This is the most accurate location and should be used prior
    to falling back to the lineNumber. Only if this coordinate does not exist in the comparing schema, should the line number be used.
    """
    schemaCoordinate: String

    """
    Name of the service if reviewing a specific service's schema.
    Else an empty string.
    """
    serviceName: String!

    # @todo
    # """
    # The specific version of the proposal that this review is for.
    # """
    # schemaProposalVersion: SchemaProposalVersion

    """
    If null then this review is just a comment. Otherwise, the reviewer changed the state of the
    proposal as part of their review. E.g. The reviewer can approve a version with a comment.
    """
    stageTransition: SchemaProposalStage

    """
    The stored author of this review. Does not update if a user changes their name for the time being.
    """
    author: String!
  }

  type SchemaProposalComment {
    id: ID!

    """
    When the comment was initially posted
    """
    createdAt: DateTime!

    """
    Content of this comment. E.g. "Nice job!"
    """
    body: String!

    """
    If edited, then when it was last edited.
    """
    updatedAt: DateTime

    """
    The stored author of this comment. Does not update if a user changes their name for the time being.
    """
    author: String!
  }

  extend type SchemaChange {
    meta: SchemaChangeMeta
  }

  union SchemaChangeMeta =
    | FieldArgumentDefaultChanged
    | FieldArgumentDescriptionChanged
    | FieldArgumentTypeChanged
    | DirectiveRemoved
    | DirectiveAdded
    | DirectiveDescriptionChanged
    | DirectiveLocationAdded
    | DirectiveLocationRemoved
    | DirectiveArgumentAdded
    | DirectiveArgumentRemoved
    | DirectiveArgumentDescriptionChanged
    | DirectiveArgumentDefaultValueChanged
    | DirectiveArgumentTypeChanged
    | EnumValueRemoved
    | EnumValueAdded
    | EnumValueDescriptionChanged
    | EnumValueDeprecationReasonChanged
    | EnumValueDeprecationReasonAdded
    | EnumValueDeprecationReasonRemoved
    | FieldRemoved
    | FieldAdded
    | FieldDescriptionChanged
    | FieldDescriptionAdded
    | FieldDescriptionRemoved
    | FieldDeprecationAdded
    | FieldDeprecationRemoved
    | FieldDeprecationReasonChanged
    | FieldDeprecationReasonAdded
    | FieldDeprecationReasonRemoved
    | FieldTypeChanged
    | DirectiveUsageUnionMemberAdded
    | DirectiveUsageUnionMemberRemoved
    | FieldArgumentAdded
    | FieldArgumentRemoved
    | InputFieldRemoved
    | InputFieldAdded
    | InputFieldDescriptionAdded
    | InputFieldDescriptionRemoved
    | InputFieldDescriptionChanged
    | InputFieldDefaultValueChanged
    | InputFieldTypeChanged
    | ObjectTypeInterfaceAdded
    | ObjectTypeInterfaceRemoved
    | SchemaQueryTypeChanged
    | SchemaMutationTypeChanged
    | SchemaSubscriptionTypeChanged
    | TypeRemoved
    | TypeAdded
    | TypeKindChanged
    | TypeDescriptionChanged
    | TypeDescriptionAdded
    | TypeDescriptionRemoved
    | UnionMemberRemoved
    | UnionMemberAdded
    | DirectiveUsageEnumAdded
    | DirectiveUsageEnumRemoved
    | DirectiveUsageEnumValueAdded
    | DirectiveUsageEnumValueRemoved
    | DirectiveUsageInputObjectRemoved
    | DirectiveUsageInputObjectAdded
    | DirectiveUsageInputFieldDefinitionAdded
    | DirectiveUsageInputFieldDefinitionRemoved
    | DirectiveUsageFieldAdded
    | DirectiveUsageFieldRemoved
    | DirectiveUsageScalarAdded
    | DirectiveUsageScalarRemoved
    | DirectiveUsageObjectAdded
    | DirectiveUsageObjectRemoved
    | DirectiveUsageInterfaceAdded
    | DirectiveUsageSchemaAdded
    | DirectiveUsageSchemaRemoved
    | DirectiveUsageFieldDefinitionAdded
    | DirectiveUsageFieldDefinitionRemoved
    | DirectiveUsageArgumentDefinitionRemoved
    | DirectiveUsageInterfaceRemoved
    | DirectiveUsageArgumentDefinitionAdded
    | DirectiveUsageArgumentAdded
    | DirectiveUsageArgumentRemoved
    | DirectiveRepeatableAdded
    | DirectiveRepeatableRemoved

  # Directive

  type FieldArgumentDescriptionChanged {
    typeName: String!
    fieldName: String!
    argumentName: String!
    oldDescription: String
    newDescription: String
  }

  type FieldArgumentDefaultChanged {
    typeName: String!
    fieldName: String!
    argumentName: String!
    oldDefaultValue: String
    newDefaultValue: String
  }

  type FieldArgumentTypeChanged {
    typeName: String!
    fieldName: String!
    argumentName: String!
    oldArgumentType: String!
    newArgumentType: String!
    isSafeArgumentTypeChange: Boolean
  }

  type DirectiveRemoved {
    removedDirectiveName: String!
  }

  type DirectiveAdded {
    addedDirectiveName: String!
    addedDirectiveRepeatable: Boolean
    addedDirectiveLocations: [String!]!
    addedDirectiveDescription: String
  }

  type DirectiveDescriptionChanged {
    directiveName: String!
    oldDirectiveDescription: String
    newDirectiveDescription: String
  }

  type DirectiveLocationAdded {
    directiveName: String!
    addedDirectiveLocation: String!
  }

  type DirectiveLocationRemoved {
    directiveName: String!
    removedDirectiveLocation: String!
  }

  type DirectiveArgumentAdded {
    directiveName: String!
    addedDirectiveArgumentName: String!
    addedDirectiveArgumentTypeIsNonNull: Boolean
    addedToNewDirective: Boolean
    addedDirectiveArgumentDescription: String
    addedDirectiveArgumentType: String!
    addedDirectiveDefaultValue: String
  }

  type DirectiveArgumentRemoved {
    directiveName: String!
    removedDirectiveArgumentName: String!
  }

  type DirectiveArgumentDescriptionChanged {
    directiveName: String!
    directiveArgumentName: String!
    oldDirectiveArgumentDescription: String
    newDirectiveArgumentDescription: String
  }

  type DirectiveArgumentDefaultValueChanged {
    directiveName: String!
    directiveArgumentName: String!
    oldDirectiveArgumentDefaultValue: String
    newDirectiveArgumentDefaultValue: String
  }

  type DirectiveArgumentTypeChanged {
    directiveName: String!
    directiveArgumentName: String!
    oldDirectiveArgumentType: String!
    newDirectiveArgumentType: String!
    isSafeDirectiveArgumentTypeChange: Boolean
  }

  # Enum

  type EnumValueRemoved {
    enumName: String!
    removedEnumValueName: String!
    isEnumValueDeprecated: Boolean
  }

  type EnumValueAdded {
    enumName: String!
    addedEnumValueName: String!
    addedToNewType: Boolean
    addedDirectiveDescription: String
  }

  type EnumValueDescriptionChanged {
    enumName: String!
    enumValueName: String!
    oldEnumValueDescription: String
    newEnumValueDescription: String
  }

  type EnumValueDeprecationReasonChanged {
    enumName: String!
    enumValueName: String!
    oldEnumValueDeprecationReason: String!
    newEnumValueDeprecationReason: String!
  }

  type EnumValueDeprecationReasonAdded {
    enumName: String!
    enumValueName: String!
    addedValueDeprecationReason: String!
  }

  type EnumValueDeprecationReasonRemoved {
    enumName: String!
    enumValueName: String!
    removedEnumValueDeprecationReason: String!
  }

  # Field

  type FieldRemoved {
    typeName: String!
    removedFieldName: String!
    isRemovedFieldDeprecated: Boolean
    typeType: String!
  }

  type FieldAdded {
    typeName: String!
    addedFieldName: String!
    typeType: String!
    addedFieldReturnType: String!
  }

  type FieldDescriptionChanged {
    typeName: String!
    fieldName: String!
    oldDescription: String
    newDescription: String
  }

  type FieldDescriptionAdded {
    typeName: String!
    fieldName: String!
    addedDescription: String!
  }

  type FieldDescriptionRemoved {
    typeName: String!
    fieldName: String!
  }

  type FieldDeprecationAdded {
    typeName: String!
    fieldName: String!
    deprecationReason: String!
  }

  type FieldDeprecationRemoved {
    typeName: String!
    fieldName: String!
  }

  type FieldDeprecationReasonChanged {
    typeName: String!
    fieldName: String!
    oldDeprecationReason: String!
    newDeprecationReason: String!
  }

  type FieldDeprecationReasonAdded {
    typeName: String!
    fieldName: String!
    addedDeprecationReason: String!
  }

  type FieldDeprecationReasonRemoved {
    typeName: String!
    fieldName: String!
  }

  type FieldTypeChanged {
    typeName: String!
    fieldName: String!
    oldFieldType: String!
    newFieldType: String!
    isSafeFieldTypeChange: Boolean
  }

  type DirectiveUsageUnionMemberAdded {
    unionName: String!
    addedUnionMemberTypeName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageUnionMemberRemoved {
    unionName: String!
    removedUnionMemberTypeName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type FieldArgumentAdded {
    typeName: String!
    fieldName: String!
    addedArgumentName: String!
    addedArgumentType: String!
    hasDefaultValue: Boolean
    isAddedFieldArgumentBreaking: Boolean
    addedToNewField: Boolean
  }

  type FieldArgumentRemoved {
    typeName: String!
    fieldName: String!
    removedFieldArgumentName: String!
    removedFieldType: String!
  }

  # Input

  type InputFieldRemoved {
    inputName: String!
    removedFieldName: String!
    isInputFieldDeprecated: Boolean
  }

  type InputFieldAdded {
    inputName: String!
    addedInputFieldName: String!
    isAddedInputFieldTypeNullable: Boolean
    addedInputFieldType: String!
    addedFieldDefault: String
    addedToNewType: Boolean
  }

  type InputFieldDescriptionAdded {
    inputName: String!
    inputFieldName: String!
    addedInputFieldDescription: String!
  }

  type InputFieldDescriptionRemoved {
    inputName: String!
    inputFieldName: String!
    removedDescription: String!
  }

  type InputFieldDescriptionChanged {
    inputName: String!
    inputFieldName: String!
    oldInputFieldDescription: String!
    newInputFieldDescription: String!
  }

  type InputFieldDefaultValueChanged {
    inputName: String!
    inputFieldName: String!
    oldDefaultValue: String
    newDefaultValue: String
  }

  type InputFieldTypeChanged {
    inputName: String!
    inputFieldName: String!
    oldInputFieldType: String!
    newInputFieldType: String!
    isInputFieldTypeChangeSafe: Boolean
  }

  # Type

  type ObjectTypeInterfaceAdded {
    objectTypeName: String!
    addedInterfaceName: String!
    addedToNewType: Boolean
  }

  type ObjectTypeInterfaceRemoved {
    objectTypeName: String!
    removedInterfaceName: String!
  }

  # Schema

  type SchemaQueryTypeChanged {
    oldQueryTypeName: String!
    newQueryTypeName: String!
  }

  type SchemaMutationTypeChanged {
    oldMutationTypeName: String!
    newMutationTypeName: String!
  }

  type SchemaSubscriptionTypeChanged {
    oldSubscriptionTypeName: String!
    newSubscriptionTypeName: String!
  }

  # Type

  type TypeRemoved {
    removedTypeName: String!
  }

  type TypeAdded {
    addedTypeName: String!
    addedTypeKind: String!
  }

  type TypeKindChanged {
    typeName: String!
    oldTypeKind: String!
    newTypeKind: String!
  }

  type TypeDescriptionChanged {
    typeName: String!
    oldTypeDescription: String!
    newTypeDescription: String!
  }

  type TypeDescriptionAdded {
    typeName: String!
    addedTypeDescription: String!
  }

  type TypeDescriptionRemoved {
    typeName: String!
    removedTypeDescription: String!
  }

  # Union

  type UnionMemberRemoved {
    unionName: String!
    removedUnionMemberTypeName: String!
  }

  type UnionMemberAdded {
    unionName: String!
    addedUnionMemberTypeName: String!
    addedToNewType: Boolean
  }

  # Directive Usage

  type DirectiveUsageEnumAdded {
    enumName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageEnumRemoved {
    enumName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageEnumValueAdded {
    enumName: String!
    enumValueName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageEnumValueRemoved {
    enumName: String!
    enumValueName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageInputObjectRemoved {
    inputObjectName: String!
    removedInputFieldName: String!
    isRemovedInputFieldTypeNullable: Boolean
    removedInputFieldType: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageInputObjectAdded {
    inputObjectName: String!
    addedInputFieldName: String!
    isAddedInputFieldTypeNullable: Boolean
    addedInputFieldType: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageInputFieldDefinitionAdded {
    inputObjectName: String!
    inputFieldName: String!
    inputFieldType: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageInputFieldDefinitionRemoved {
    inputObjectName: String!
    inputFieldName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageFieldAdded {
    typeName: String!
    fieldName: String!
    addedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageFieldRemoved {
    typeName: String!
    fieldName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageScalarAdded {
    scalarName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageScalarRemoved {
    scalarName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageObjectAdded {
    objectName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageObjectRemoved {
    objectName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageInterfaceAdded {
    interfaceName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageSchemaAdded {
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageSchemaRemoved {
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageFieldDefinitionAdded {
    typeName: String!
    fieldName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageFieldDefinitionRemoved {
    typeName: String!
    fieldName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageArgumentDefinitionRemoved {
    typeName: String!
    fieldName: String!
    argumentName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageInterfaceRemoved {
    interfaceName: String!
    removedDirectiveName: String!
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageArgumentDefinitionAdded {
    typeName: String!
    fieldName: String!
    argumentName: String!
    addedDirectiveName: String!
    addedToNewType: Boolean
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageArgumentAdded {
    directiveName: String!
    addedArgumentName: String!
    addedArgumentValue: String!
    oldArgumentValue: String
    parentTypeName: String
    parentFieldName: String
    parentArgumentName: String
    parentEnumValueName: String
    directiveRepeatedTimes: Int!
  }

  type DirectiveUsageArgumentRemoved {
    directiveName: String!
    removedArgumentName: String!
    parentTypeName: String
    parentFieldName: String
    parentArgumentName: String
    parentEnumValueName: String
    directiveRepeatedTimes: Int!
  }

  type DirectiveRepeatableAdded {
    directiveName: String!
  }

  type DirectiveRepeatableRemoved {
    directiveName: String!
  }
`;
