import { gql } from "@apollo/client";
import { ExportTypes, MultipageAnnotationJson } from "../components/types";
import {
  AnalysisType,
  AnnotationLabelType,
  ColumnType,
  CorpusType,
  DatacellType,
  DocumentType,
  ExtractType,
  FeedbackType,
  FieldsetType,
  LabelSetType,
  LabelType,
  Maybe,
  UserExportType,
  CorpusActionType,
  ResearchReportType,
  JobStatus,
} from "../types/graphql-api";

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// LOGIN-RELATED MUTATIONS
///
/// Only used if USE_AUTH0 is set to false
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
export interface LoginInputs {
  username: string;
  password: string;
}

export interface LoginOutputs {
  tokenAuth: {
    token: string;
    refreshExpiresIn: number;
    payload: string;
    user: {
      id: string;
      email: string;
      name: string;
      username: string;
      // Nullable per schema; login is always self-view so value is defined here.
      isUsageCapped: Maybe<boolean>;
      isSuperuser: boolean;
    };
  };
}

export const LOGIN_MUTATION = gql`
  mutation ($username: String!, $password: String!) {
    tokenAuth(username: $username, password: $password) {
      token
      refreshExpiresIn
      payload
      user {
        id
        email
        name
        username
        isUsageCapped
        isSuperuser
      }
    }
  }
`;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CORPUS-RELATED MUTATIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

export interface DeleteCorpusInputs {
  id: string;
}

export interface DeleteCorpusOutputs {
  deleteCorpus: {
    ok?: boolean;
    message?: string;
  };
}

export const DELETE_CORPUS = gql`
  mutation ($id: String!) {
    deleteCorpus(id: $id) {
      ok
      message
    }
  }
`;

export interface UpdateCorpusInputs {
  id: string;
  title?: string;
  description?: string;
  icon?: string;
  filename?: string;
  preferredEmbedder?: string;
  // pydantic-ai model spec ("provider:model") for this corpus's agents. Empty
  // string clears it and falls back to the install-wide default LLM.
  preferredLlm?: string;
  labelSet?: string;
  slug?: string;
  // NOTE: isPublic removed - use SET_CORPUS_VISIBILITY mutation instead
  corpusAgentInstructions?: string;
  documentAgentInstructions?: string;
  categories?: string[];
  license?: string;
  licenseLink?: string;
}

export interface UpdateCorpusOutputs {
  updateCorpus: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      title: string;
      description: string;
      icon?: string;
      labelSet?: LabelSetType;
    };
  };
}

export const UPDATE_CORPUS = gql`
  mutation (
    $id: String!
    $icon: String
    $description: String
    $labelSet: String
    $title: String
    $preferredEmbedder: String
    $preferredLlm: String
    $slug: String
    $corpusAgentInstructions: String
    $documentAgentInstructions: String
    $categories: [ID]
    $license: String
    $licenseLink: String
  ) {
    updateCorpus(
      id: $id
      icon: $icon
      description: $description
      labelSet: $labelSet
      title: $title
      preferredEmbedder: $preferredEmbedder
      preferredLlm: $preferredLlm
      slug: $slug
      corpusAgentInstructions: $corpusAgentInstructions
      documentAgentInstructions: $documentAgentInstructions
      categories: $categories
      license: $license
      licenseLink: $licenseLink
    ) {
      ok
      message
    }
  }
`;

// NOTE: Use SET_CORPUS_VISIBILITY to change corpus visibility (isPublic)
// This mutation has proper permission checks (owner OR PERMISSION permission)
export interface SetCorpusVisibilityInputs {
  corpusId: string;
  isPublic: boolean;
}

export interface SetCorpusVisibilityOutputs {
  setCorpusVisibility: {
    ok: boolean;
    message: string;
  };
}

export const SET_CORPUS_VISIBILITY = gql`
  mutation SetCorpusVisibility($corpusId: ID!, $isPublic: Boolean!) {
    setCorpusVisibility(corpusId: $corpusId, isPublic: $isPublic) {
      ok
      message
    }
  }
`;

export const UPDATE_CORPUS_DESCRIPTION = gql`
  mutation UpdateCorpusDescription($corpusId: ID!, $newContent: String!) {
    updateCorpusDescription(corpusId: $corpusId, newContent: $newContent) {
      ok
      message
      version
      obj {
        id
        title
        description
        descriptionRevisions {
          id
          version
          author {
            id
            slug
            email
          }
          created
          snapshot
        }
      }
    }
  }
`;

export interface UpdateCorpusDescriptionInputs {
  corpusId: string;
  newContent: string;
}

export interface UpdateCorpusDescriptionOutputs {
  updateCorpusDescription: {
    ok: boolean;
    message: string;
    version?: number;
    obj?: {
      id: string;
      title: string;
      description: string;
      descriptionRevisions: Array<{
        id: string;
        version: number;
        author: {
          id: string;
          email: string;
        };
        created: string;
        snapshot?: string;
      }>;
    };
  };
}

export interface CreateCorpusInputs {
  title?: string;
  description?: string;
  icon?: string;
  filename?: string;
  labelSet?: string;
  preferredEmbedder?: string;
  categories?: string[];
  license?: string;
  licenseLink?: string;
}

export interface CreateCorpusOutputs {
  createCorpus: {
    ok?: boolean;
    message?: string;
    /** Global id of the created corpus — lets follow-up mutations (e.g.
     * setupCorpusIntelligence) chain off the create. */
    objId?: string | null;
  };
}

export const CREATE_CORPUS = gql`
  mutation (
    $description: String
    $icon: String
    $labelSet: String
    $title: String
    $preferredEmbedder: String
    $slug: String
    $categories: [ID]
    $license: String
    $licenseLink: String
  ) {
    createCorpus(
      description: $description
      icon: $icon
      labelSet: $labelSet
      title: $title
      preferredEmbedder: $preferredEmbedder
      slug: $slug
      categories: $categories
      license: $license
      licenseLink: $licenseLink
    ) {
      ok
      message
      objId
    }
  }
`;

// ---------------- Collection-intelligence setup ----------------
// One-click composite: installs the reference-enrichment add_document action
// and the description/summary agent templates, starts the first reference
// weave, and batch-runs the agents over every document already present.
// Idempotent server-side — safe to call repeatedly.

export interface IntelligenceTemplateOutcome {
  templateName: string;
  installedNow: boolean;
  alreadyInstalled: boolean;
  queuedCount: number;
  skippedAlreadyRunCount: number;
  error: string;
  /** Documents deferred past the per-call batch cap — re-run to continue. */
  remainingCount: number;
}

export interface SetupCorpusIntelligenceInputs {
  corpusId: string;
}

export interface SetupCorpusIntelligenceOutputs {
  setupCorpusIntelligence: {
    ok: boolean;
    message?: string | null;
    summary?: {
      referenceAvailable: boolean;
      referenceActionInstalledNow: boolean;
      referenceActionAlreadyInstalled: boolean;
      referenceAnalysisStarted: boolean;
      totalActiveDocuments: number;
      templates: IntelligenceTemplateOutcome[];
    } | null;
  };
}

export const SETUP_CORPUS_INTELLIGENCE = gql`
  mutation setupCorpusIntelligence($corpusId: ID!) {
    setupCorpusIntelligence(corpusId: $corpusId) {
      ok
      message
      summary {
        referenceAvailable
        referenceActionInstalledNow
        referenceActionAlreadyInstalled
        referenceAnalysisStarted
        totalActiveDocuments
        templates {
          templateName
          installedNow
          alreadyInstalled
          queuedCount
          skippedAlreadyRunCount
          error
          remainingCount
        }
      }
    }
  }
`;

export interface StartExportCorpusInputs {
  corpusId: string;
  exportFormat: ExportTypes;
  postProcessors?: string[];
  inputKwargs?: Record<any, any>;
}

export interface StartExportCorpusOutputs {
  exportCorpus: {
    ok?: boolean;
    message?: string;
    export?: Maybe<UserExportType>;
  };
}

export interface RunCorpusEnrichmentInputs {
  corpusId: string;
  runEnrichment?: boolean;
  runCrawl?: boolean;
  options?: {
    referenceTypes?: string[];
    useLlmTier?: boolean;
    maxDepth?: number;
    minDemand?: number;
    maxAuthorities?: number;
    perJurisdictionCap?: number;
    tokenBudget?: number;
  };
}

export interface EnrichmentAnalysisRow {
  id: string;
  status: string;
  analysisStarted?: string | null;
  analysisCompleted?: string | null;
  errorMessage?: string | null;
  resultMessage?: string | null;
  analyzer: { id: string; taskName: string };
}

export interface RunCorpusEnrichmentOutputs {
  runCorpusEnrichment: {
    ok: boolean;
    message?: string | null;
    /**
     * True when some jobs dispatched but others failed (only meaningful when
     * `ok` is true). Lets the UI show `message` as a warning without coupling
     * to its text.
     */
    partial?: boolean | null;
    analyses: EnrichmentAnalysisRow[];
  };
}

export const RUN_CORPUS_ENRICHMENT = gql`
  mutation RunCorpusEnrichment(
    $corpusId: ID!
    $runEnrichment: Boolean
    $runCrawl: Boolean
    $options: RunEnrichmentOptionsInput
  ) {
    runCorpusEnrichment(
      corpusId: $corpusId
      runEnrichment: $runEnrichment
      runCrawl: $runCrawl
      options: $options
    ) {
      ok
      message
      partial
      analyses {
        id
        status
        analysisStarted
        analysisCompleted
        errorMessage
        resultMessage
        analyzer {
          id
          taskName
        }
      }
    }
  }
`;

export interface RunAuthorityDiscoveryInputs {
  /** Global IDs of the AuthorityFrontier rows to run discovery on. */
  frontierIds: string[];
}

export interface RunAuthorityDiscoveryOutputs {
  runAuthorityDiscovery: {
    ok: boolean;
    message?: string | null;
    count: number;
  };
}

/**
 * Run authority discovery on a hand-picked subset of AuthorityFrontier rows
 * (superuser-only; fire-and-forget). Depth 0 — ingests exactly the selected
 * rows. The monitor reflects each row's discovery_state as it transitions.
 */
export const RUN_AUTHORITY_DISCOVERY = gql`
  mutation RunAuthorityDiscovery($frontierIds: [ID!]!) {
    runAuthorityDiscovery(frontierIds: $frontierIds) {
      ok
      message
      count
    }
  }
`;

// --- Authority key-equivalence mutations (superuser-only) ------------------
// CRUD over the manually-curated act-section ↔ USC/CFR key bridges shown in the
// Authority Console Aliases & Relationships tab (/admin/authority/mappings).
// Only ``source = "manual"`` rows can be created,
// updated, or deleted; the backend enforces this and returns ok:false with an
// opaque message otherwise. The returned ``obj`` lets the panel refresh a row
// in place without a full refetch.

export interface AuthorityKeyEquivalenceMutationObj {
  id: string;
  fromKey: string;
  toKey: string;
  source: string;
  confidence?: number | null;
  note?: string | null;
  editable: boolean;
  createdByUsername?: string | null;
  modified?: string | null;
}

export interface CreateAuthorityKeyEquivalenceInputs {
  fromKey: string;
  toKey: string;
  note?: string | null;
}

export interface CreateAuthorityKeyEquivalenceOutputs {
  createAuthorityKeyEquivalence: {
    ok: boolean;
    message?: string | null;
    obj?: AuthorityKeyEquivalenceMutationObj | null;
  };
}

export const CREATE_AUTHORITY_KEY_EQUIVALENCE = gql`
  mutation CreateAuthorityKeyEquivalence(
    $fromKey: String!
    $toKey: String!
    $note: String
  ) {
    createAuthorityKeyEquivalence(
      fromKey: $fromKey
      toKey: $toKey
      note: $note
    ) {
      ok
      message
      obj {
        id
        fromKey
        toKey
        source
        confidence
        note
        editable
        createdByUsername
        modified
      }
    }
  }
`;

export interface UpdateAuthorityKeyEquivalenceInputs {
  id: string;
  fromKey?: string | null;
  toKey?: string | null;
  note?: string | null;
}

export interface UpdateAuthorityKeyEquivalenceOutputs {
  updateAuthorityKeyEquivalence: {
    ok: boolean;
    message?: string | null;
    obj?: AuthorityKeyEquivalenceMutationObj | null;
  };
}

export const UPDATE_AUTHORITY_KEY_EQUIVALENCE = gql`
  mutation UpdateAuthorityKeyEquivalence(
    $id: ID!
    $fromKey: String
    $toKey: String
    $note: String
  ) {
    updateAuthorityKeyEquivalence(
      id: $id
      fromKey: $fromKey
      toKey: $toKey
      note: $note
    ) {
      ok
      message
      obj {
        id
        fromKey
        toKey
        source
        confidence
        note
        editable
        createdByUsername
        modified
      }
    }
  }
`;

export interface DeleteAuthorityKeyEquivalenceInputs {
  id: string;
}

export interface DeleteAuthorityKeyEquivalenceOutputs {
  deleteAuthorityKeyEquivalence: {
    ok: boolean;
    message?: string | null;
  };
}

export const DELETE_AUTHORITY_KEY_EQUIVALENCE = gql`
  mutation DeleteAuthorityKeyEquivalence($id: ID!) {
    deleteAuthorityKeyEquivalence(id: $id) {
      ok
      message
    }
  }
`;

// ---- Authority Namespace CRUD (the registry of bodies of law) ------------- //

export interface AuthorityNamespaceMutationObj {
  id: string;
  prefix: string;
  displayName: string;
  jurisdiction?: string | null;
  authorityType?: string | null;
  scope: string;
  source: string;
  aliases: string[];
  provider?: string | null;
  sourceRootUrl?: string | null;
  license?: string | null;
  isGlobal: boolean;
  createdByUsername?: string | null;
  modified?: string | null;
}

const _NAMESPACE_OBJ_FIELDS = `
  id
  prefix
  displayName
  jurisdiction
  authorityType
  scope
  source
  aliases
  provider
  sourceRootUrl
  license
  isGlobal
  createdByUsername
  modified
`;

export interface CreateAuthorityNamespaceInputs {
  prefix: string;
  displayName: string;
  jurisdiction?: string | null;
  authorityType?: string | null;
  aliases?: string[] | null;
  isGlobal?: boolean | null;
  authorityCorpusId?: string | null;
  provider?: string | null;
  sourceRootUrl?: string | null;
  license?: string | null;
}

export interface CreateAuthorityNamespaceOutputs {
  createAuthorityNamespace: {
    ok: boolean;
    message?: string | null;
    obj?: AuthorityNamespaceMutationObj | null;
  };
}

export const CREATE_AUTHORITY_NAMESPACE = gql`
  mutation CreateAuthorityNamespace(
    $prefix: String!
    $displayName: String!
    $jurisdiction: String
    $authorityType: String
    $aliases: [String]
    $isGlobal: Boolean
    $authorityCorpusId: ID
    $provider: String
    $sourceRootUrl: String
    $license: String
  ) {
    createAuthorityNamespace(
      prefix: $prefix
      displayName: $displayName
      jurisdiction: $jurisdiction
      authorityType: $authorityType
      aliases: $aliases
      isGlobal: $isGlobal
      authorityCorpusId: $authorityCorpusId
      provider: $provider
      sourceRootUrl: $sourceRootUrl
      license: $license
    ) {
      ok
      message
      obj {
        ${_NAMESPACE_OBJ_FIELDS}
      }
    }
  }
`;

export interface UpdateAuthorityNamespaceInputs {
  id: string;
  displayName?: string | null;
  jurisdiction?: string | null;
  authorityType?: string | null;
  aliases?: string[] | null;
  isGlobal?: boolean | null;
  authorityCorpusId?: string | null;
  provider?: string | null;
  sourceRootUrl?: string | null;
  license?: string | null;
}

export interface UpdateAuthorityNamespaceOutputs {
  updateAuthorityNamespace: {
    ok: boolean;
    message?: string | null;
    obj?: AuthorityNamespaceMutationObj | null;
  };
}

export const UPDATE_AUTHORITY_NAMESPACE = gql`
  mutation UpdateAuthorityNamespace(
    $id: ID!
    $displayName: String
    $jurisdiction: String
    $authorityType: String
    $aliases: [String]
    $isGlobal: Boolean
    $authorityCorpusId: ID
    $provider: String
    $sourceRootUrl: String
    $license: String
  ) {
    updateAuthorityNamespace(
      id: $id
      displayName: $displayName
      jurisdiction: $jurisdiction
      authorityType: $authorityType
      aliases: $aliases
      isGlobal: $isGlobal
      authorityCorpusId: $authorityCorpusId
      provider: $provider
      sourceRootUrl: $sourceRootUrl
      license: $license
    ) {
      ok
      message
      obj {
        ${_NAMESPACE_OBJ_FIELDS}
      }
    }
  }
`;

export interface SetAuthorityNamespaceAliasesInputs {
  id: string;
  aliases: string[];
}

export interface SetAuthorityNamespaceAliasesOutputs {
  setAuthorityNamespaceAliases: {
    ok: boolean;
    message?: string | null;
    obj?: AuthorityNamespaceMutationObj | null;
  };
}

export const SET_AUTHORITY_NAMESPACE_ALIASES = gql`
  mutation SetAuthorityNamespaceAliases($id: ID!, $aliases: [String]!) {
    setAuthorityNamespaceAliases(id: $id, aliases: $aliases) {
      ok
      message
      obj {
        ${_NAMESPACE_OBJ_FIELDS}
      }
    }
  }
`;

export interface DeleteAuthorityNamespaceInputs {
  id: string;
}

export interface DeleteAuthorityNamespaceOutputs {
  deleteAuthorityNamespace: {
    ok: boolean;
    message?: string | null;
  };
}

export const DELETE_AUTHORITY_NAMESPACE = gql`
  mutation DeleteAuthorityNamespace($id: ID!) {
    deleteAuthorityNamespace(id: $id) {
      ok
      message
    }
  }
`;

// ---- AuthorityFrontier admin row actions (discovery queue) ---------------- //

export interface FrontierActionObj {
  id: string;
  discoveryState: string;
  provider?: string | null;
  lastError?: string | null;
  ingestedDocument?: { id: string } | null;
}

interface FrontierActionOutput {
  ok: boolean;
  message?: string | null;
  obj?: FrontierActionObj | null;
}

const _FRONTIER_ACTION_FIELDS = `
  id
  discoveryState
  provider
  lastError
  ingestedDocument {
    id
  }
`;

export interface RequeueAuthorityFrontierOutputs {
  requeueAuthorityFrontier: FrontierActionOutput;
}
export const REQUEUE_AUTHORITY_FRONTIER = gql`
  mutation RequeueAuthorityFrontier($id: ID!) {
    requeueAuthorityFrontier(id: $id) {
      ok
      message
      obj {
        ${_FRONTIER_ACTION_FIELDS}
      }
    }
  }
`;

export interface ResetAuthorityFrontierOutputs {
  resetAuthorityFrontier: FrontierActionOutput;
}
export const RESET_AUTHORITY_FRONTIER = gql`
  mutation ResetAuthorityFrontier($id: ID!) {
    resetAuthorityFrontier(id: $id) {
      ok
      message
      obj {
        ${_FRONTIER_ACTION_FIELDS}
      }
    }
  }
`;

export interface ApproveAuthorityFrontierOutputs {
  approveAuthorityFrontier: FrontierActionOutput;
}
export const APPROVE_AUTHORITY_FRONTIER = gql`
  mutation ApproveAuthorityFrontier($id: ID!) {
    approveAuthorityFrontier(id: $id) {
      ok
      message
      obj {
        ${_FRONTIER_ACTION_FIELDS}
      }
    }
  }
`;

export interface RerouteAuthorityFrontierInputs {
  id: string;
  provider: string;
}
export interface RerouteAuthorityFrontierOutputs {
  rerouteAuthorityFrontier: FrontierActionOutput;
}
export const REROUTE_AUTHORITY_FRONTIER = gql`
  mutation RerouteAuthorityFrontier($id: ID!, $provider: String!) {
    rerouteAuthorityFrontier(id: $id, provider: $provider) {
      ok
      message
      obj {
        ${_FRONTIER_ACTION_FIELDS}
      }
    }
  }
`;

export interface DeleteAuthorityFrontierInputs {
  ids: string[];
}
export interface DeleteAuthorityFrontierOutputs {
  deleteAuthorityFrontier: {
    ok: boolean;
    message?: string | null;
    count?: number | null;
  };
}
export const DELETE_AUTHORITY_FRONTIER = gql`
  mutation DeleteAuthorityFrontier($ids: [ID!]!) {
    deleteAuthorityFrontier(ids: $ids) {
      ok
      message
      count
    }
  }
`;

export const START_EXPORT_CORPUS = gql`
  mutation (
    $corpusId: String!
    $exportFormat: ExportType!
    $postProcessors: [String]
    $inputKwargs: GenericScalar
  ) {
    exportCorpus(
      corpusId: $corpusId
      exportFormat: $exportFormat
      postProcessors: $postProcessors
      inputKwargs: $inputKwargs
    ) {
      ok
      message
      export {
        id
      }
    }
  }
`;

export interface AcceptCookieConsentInputs {}

export interface AcceptCookieConsentOutputs {
  acceptCookieConsent: {
    ok?: boolean;
  };
}

export const ACCEPT_COOKIE_CONSENT = gql`
  mutation {
    acceptCookieConsent {
      ok
    }
  }
`;

export interface DeleteExportInputs {
  id: string;
}

export interface DeleteExportOutputs {
  deleteExport: {
    ok?: boolean;
    message?: string;
  };
}

export const DELETE_EXPORT = gql`
  mutation ($id: String!) {
    deleteExport(id: $id) {
      ok
      message
    }
  }
`;

// NOTE: ``importOpenContractsZip`` (corpus-export ZIP import) was migrated
// from GraphQL to multipart REST. See
// ``frontend/src/utils/importHttp.ts::importCorpusExportMultipart`` and the
// ``POST /api/imports/corpus/`` endpoint. Base64-encoding large ZIPs into a
// JSON request body crashed Apollo for files past ~100 MB.

export interface StartForkCorpusInput {
  corpusId: string;
}

export interface StartForkCorpusOutput {
  ok: boolean;
  message: string;
  newCorpus: CorpusType;
}

export const START_FORK_CORPUS = gql`
  mutation ($corpusId: String!) {
    forkCorpus(corpusId: $corpusId) {
      ok
      message
      newCorpus {
        id
        icon
        title
        description
        backendLock
        labelSet {
          id
        }
      }
    }
  }
`;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// LABELSET-RELATED MUTATIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

export interface DeleteLabelsetInputs {
  id: string;
}

export interface DeleteLabelsetOutputs {
  deleteLabelset: {
    ok?: boolean;
    message?: string;
  };
}

export const DELETE_LABELSET = gql`
  mutation ($id: String!) {
    deleteLabelset(id: $id) {
      ok
      message
    }
  }
`;

export interface CreateLabelsetInputs {
  title?: string;
  description?: string;
  base64IconString?: string;
  filename?: string;
}

export interface CreateLabelsetOutputs {
  ok?: boolean;
  message?: string;
  obj?: LabelSetType;
}

export const CREATE_LABELSET = gql`
  mutation (
    $title: String!
    $description: String
    $icon: String
    $filename: String
  ) {
    createLabelset(
      title: $title
      description: $description
      base64IconString: $icon
      filename: $filename
    ) {
      ok
      message
      obj {
        id
        title
        description
        icon
      }
    }
  }
`;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ANNOTATION LABEL-RELATED MUTATIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

export interface UpdateAnnotationLabelInputs {
  id: string;
  color?: string;
  description?: string;
  icon?: string;
  text?: string;
  labelType?: LabelType;
}

export interface UpdateAnnotationLabelOutputs {
  updateAnnotationLabel: {
    ok?: boolean;
    message?: string;
  };
}

export const UPDATE_ANNOTATION_LABEL = gql`
  mutation (
    $id: String!
    $color: String
    $description: String
    $icon: String
    $text: String
    $labelType: String
  ) {
    updateAnnotationLabel(
      color: $color
      description: $description
      icon: $icon
      id: $id
      text: $text
      labelType: $labelType
    ) {
      ok
      message
    }
  }
`;

export interface CreateAnnotationLabelForLabelsetInputs {
  color?: string;
  description?: string;
  icon?: string;
  text?: string;
  labelType?: LabelType;
  labelsetId: string;
}

export interface CreateAnnotationLabelForLabelsetOutputs {
  ok?: boolean;
  message?: string;
}

export const CREATE_ANNOTATION_LABEL_FOR_LABELSET = gql`
  mutation (
    $color: String
    $description: String
    $icon: String
    $text: String
    $labelType: String
    $labelsetId: String!
  ) {
    createAnnotationLabelForLabelset(
      color: $color
      description: $description
      icon: $icon
      text: $text
      labelType: $labelType
      labelsetId: $labelsetId
    ) {
      ok
      message
    }
  }
`;

// Smart Label Mutations
export interface SmartLabelSearchOrCreateInputs {
  corpusId: string;
  searchTerm: string;
  labelType: string;
  color?: string;
  description?: string;
  icon?: string;
  createIfNotFound?: boolean;
  labelsetTitle?: string;
  labelsetDescription?: string;
}

export interface SmartLabelSearchOrCreateOutputs {
  smartLabelSearchOrCreate: {
    ok: boolean;
    message: string;
    labels: AnnotationLabelType[];
    labelset?: LabelSetType;
    labelsetCreated: boolean;
    labelCreated: boolean;
  };
}

export const SMART_LABEL_SEARCH_OR_CREATE = gql`
  mutation (
    $corpusId: String!
    $searchTerm: String!
    $labelType: String!
    $color: String
    $description: String
    $icon: String
    $createIfNotFound: Boolean
    $labelsetTitle: String
    $labelsetDescription: String
  ) {
    smartLabelSearchOrCreate(
      corpusId: $corpusId
      searchTerm: $searchTerm
      labelType: $labelType
      color: $color
      description: $description
      icon: $icon
      createIfNotFound: $createIfNotFound
      labelsetTitle: $labelsetTitle
      labelsetDescription: $labelsetDescription
    ) {
      ok
      message
      labels {
        id
        text
        description
        color
        icon
        labelType
      }
      labelset {
        id
        title
        description
      }
      labelsetCreated
      labelCreated
    }
  }
`;

export interface DeleteMultipleAnnotationLabelInputs {
  annotationLabelIdsToDelete: string[];
}

export interface DeleteMultipleAnnotationLabelOutputs {
  deleteMultipleAnnotationLabels: {
    ok?: boolean;
    message?: string;
  };
}

export const DELETE_MULTIPLE_ANNOTATION_LABELS = gql`
  mutation ($annotationLabelIdsToDelete: [String]!) {
    deleteMultipleAnnotationLabels(
      annotationLabelIdsToDelete: $annotationLabelIdsToDelete
    ) {
      ok
      message
    }
  }
`;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// DOCUMENT-RELATED MUTATIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

export interface LinkDocumentsToCorpusInputs {
  corpusId: string;
  documentIds: string[];
}

export interface LinkDocumentsToCorpusOutputs {
  ok?: boolean;
  message?: string;
}

export const LINK_DOCUMENTS_TO_CORPUS = gql`
  mutation ($corpusId: String!, $documentIds: [String]!) {
    linkDocumentsToCorpus(corpusId: $corpusId, documentIds: $documentIds) {
      ok
      message
    }
  }
`;

export interface RemoveDocumentsFromCorpusInputs {
  corpusId: string;
  documentIdsToRemove: string[];
}

export interface RemoveDocumentsFromCorpusOutputs {
  removeDocumentsFromCorpus: {
    ok?: boolean;
    message?: string;
  };
}

export const REMOVE_DOCUMENTS_FROM_CORPUS = gql`
  mutation ($corpusId: String!, $documentIdsToRemove: [String]!) {
    removeDocumentsFromCorpus(
      corpusId: $corpusId
      documentIdsToRemove: $documentIdsToRemove
    ) {
      ok
      message
    }
  }
`;

export interface UploadDocumentInputProps {
  base64FileString: string;
  filename: string;
  customMeta: Record<string, any>;
  makePublic: boolean;
  description?: string;
  title?: string;
  addToCorpusId?: string;
  addToFolderId?: string;
  slug?: string;
}

export interface UploadDocumentOutputProps {
  uploadDocument: {
    ok: boolean;
    message: string;
    document: {
      id: string;
      icon: string;
      pdfFile: string;
      title: string;
      description: string;
      backendLock: boolean;
      fileType: string;
      docAnnotations: {
        edges: {
          node: {
            id: string;
          };
        };
      }[];
    } | null;
  };
}

export const UPLOAD_DOCUMENT = gql`
  mutation (
    $base64FileString: String!
    $filename: String!
    $customMeta: GenericScalar!
    $description: String!
    $title: String!
    $makePublic: Boolean!
    $addToCorpusId: ID
    $addToExtractId: ID
    $addToFolderId: ID
    $slug: String
  ) {
    uploadDocument(
      base64FileString: $base64FileString
      filename: $filename
      customMeta: $customMeta
      description: $description
      title: $title
      makePublic: $makePublic
      addToCorpusId: $addToCorpusId
      addToExtractId: $addToExtractId
      addToFolderId: $addToFolderId
      slug: $slug
    ) {
      ok
      message
      document {
        id
        icon
        pdfFile
        title
        description
        backendLock
        fileType
        docAnnotations {
          edges {
            node {
              id
            }
          }
        }
      }
    }
  }
`;

export interface UpdateDocumentInputs {
  id: string;
  title?: string;
  description?: string;
  pdfFile?: string;
  customMeta?: Record<string, any>;
  slug?: string;
}

export interface UpdateDocumentOutputs {
  ok?: boolean;
  message?: string;
}

export const UPDATE_DOCUMENT = gql`
  mutation (
    $id: String!
    $pdfFile: String
    $customMeta: GenericScalar
    $description: String
    $title: String
    $slug: String
  ) {
    updateDocument(
      id: $id
      pdfFile: $pdfFile
      customMeta: $customMeta
      description: $description
      title: $title
      slug: $slug
    ) {
      ok
      message
    }
  }
`;

// ---------------- User profile updates ----------------
export interface UpdateMeInputs {
  name?: string;
  firstName?: string;
  lastName?: string;
  phone?: string;
  slug?: string;
  isProfilePublic?: boolean;
  profileHeadline?: string;
  profileAboutMarkdown?: string;
  profileLinksMarkdown?: string;
}

export interface UpdateMeOutputs {
  updateMe: {
    ok: boolean;
    message?: string;
    user?: {
      id: string;
      username: string;
      slug?: string;
      name?: string;
      firstName?: string;
      lastName?: string;
      phone?: string;
      isProfilePublic?: boolean;
      profileHeadline?: string;
      profileAboutMarkdown?: string;
      profileLinksMarkdown?: string;
    };
  };
}

export const UPDATE_ME = gql`
  mutation (
    $name: String
    $firstName: String
    $lastName: String
    $phone: String
    $slug: String
    $isProfilePublic: Boolean
    $profileHeadline: String
    $profileAboutMarkdown: String
    $profileLinksMarkdown: String
  ) {
    updateMe(
      name: $name
      firstName: $firstName
      lastName: $lastName
      phone: $phone
      slug: $slug
      isProfilePublic: $isProfilePublic
      profileHeadline: $profileHeadline
      profileAboutMarkdown: $profileAboutMarkdown
      profileLinksMarkdown: $profileLinksMarkdown
    ) {
      ok
      message
      user {
        id
        username
        slug
        name
        firstName
        lastName
        phone
        isProfilePublic
        profileHeadline
        profileAboutMarkdown
        profileLinksMarkdown
      }
    }
  }
`;

export interface DeleteMultipleDocumentsInputs {
  documentIdsToDelete: string[];
}

export interface DeleteMultipleDocumentsOutputs {
  ok?: boolean;
  message?: string;
}

export const DELETE_MULTIPLE_DOCUMENTS = gql`
  mutation ($documentIdsToDelete: [String]!) {
    deleteMultipleDocuments(documentIdsToDelete: $documentIdsToDelete) {
      ok
      message
    }
  }
`;

export interface RetryDocumentProcessingOutputType {
  retryDocumentProcessing: {
    ok: boolean;
    message: string;
    document: DocumentType | null;
  };
}

export interface RetryDocumentProcessingInputType {
  documentId: string;
}

export const RETRY_DOCUMENT_PROCESSING = gql`
  mutation ($documentId: String!) {
    retryDocumentProcessing(documentId: $documentId) {
      ok
      message
      document {
        id
        backendLock
        processingStatus
        processingError
        canRetry
      }
    }
  }
`;

export interface NewAnnotationOutputType {
  addAnnotation: {
    ok: boolean;
    annotation: {
      id: string;
      page: number;
      rawText: string;
      json: MultipageAnnotationJson;
      linkUrl: string | null;
      annotationType: LabelType;
      annotationLabel: AnnotationLabelType;
      myPermissions: string[];
      isPublic: boolean;
      sourceNodeInRelationships: {
        edges: [
          {
            node: {
              id: string;
            };
          }
        ];
      };
    };
  };
}

export interface NewAnnotationInputType {
  page: number;
  json: MultipageAnnotationJson;
  rawText: string;
  corpusId: string;
  documentId: string;
  annotationLabelId: string;
  annotationType: LabelType;
  linkUrl?: string | null;
}

export const REQUEST_ADD_ANNOTATION = gql`
  mutation (
    $json: GenericScalar!
    $page: Int!
    $rawText: String!
    $corpusId: String!
    $documentId: String!
    $annotationLabelId: String!
    $annotationType: LabelType!
    $linkUrl: String
  ) {
    addAnnotation(
      json: $json
      page: $page
      rawText: $rawText
      corpusId: $corpusId
      documentId: $documentId
      annotationLabelId: $annotationLabelId
      annotationType: $annotationType
      linkUrl: $linkUrl
    ) {
      ok
      annotation {
        id
        page
        rawText
        json
        linkUrl
        isPublic
        myPermissions
        annotationType
        annotationLabel {
          id
          icon
          description
          color
          text
          labelType
        }
        sourceNodeInRelationships {
          edges {
            node {
              id
            }
          }
        }
      }
    }
  }
`;

export interface NewUrlAnnotationOutputType {
  addUrlAnnotation: {
    ok: boolean;
    message?: string;
    annotation: {
      id: string;
      page: number;
      rawText: string;
      json: MultipageAnnotationJson;
      // Server schema returns nullable String for ``link_url``. Even though
      // ``addUrlAnnotation`` always requires a URL, narrowing this to
      // ``string`` could mask a downstream issue if the API ever omits it.
      linkUrl: string | null;
      annotationType: LabelType;
      annotationLabel: AnnotationLabelType;
      myPermissions: string[];
      isPublic: boolean;
    } | null;
  };
}

export interface NewUrlAnnotationInputType {
  page: number;
  json: MultipageAnnotationJson;
  rawText: string;
  corpusId: string;
  documentId: string;
  annotationType: LabelType;
  linkUrl: string;
}

export const REQUEST_ADD_URL_ANNOTATION = gql`
  mutation (
    $json: GenericScalar!
    $page: Int!
    $rawText: String!
    $corpusId: String!
    $documentId: String!
    $annotationType: LabelType!
    $linkUrl: String!
  ) {
    addUrlAnnotation(
      json: $json
      page: $page
      rawText: $rawText
      corpusId: $corpusId
      documentId: $documentId
      annotationType: $annotationType
      linkUrl: $linkUrl
    ) {
      ok
      message
      annotation {
        id
        page
        rawText
        json
        linkUrl
        isPublic
        myPermissions
        annotationType
        annotationLabel {
          id
          icon
          description
          color
          text
          labelType
        }
      }
    }
  }
`;

export interface NewDocTypeAnnotationOutputType {
  addDocTypeAnnotation: {
    ok: boolean;
    annotation: {
      id: string;
      myPermissions?: string[];
      isPublic?: boolean;
      annotationLabel: AnnotationLabelType;
    };
  };
}

export interface NewDocTypeAnnotationInputType {
  corpusId: string;
  documentId: string;
  annotationLabelId: string;
}

export const REQUEST_ADD_DOC_TYPE_ANNOTATION = gql`
  mutation (
    $corpusId: String!
    $documentId: String!
    $annotationLabelId: String!
  ) {
    addDocTypeAnnotation(
      corpusId: $corpusId
      documentId: $documentId
      annotationLabelId: $annotationLabelId
    ) {
      ok
      annotation {
        id
        isPublic
        myPermissions
        annotationLabel {
          id
          icon
          description
          color
          text
          labelType
        }
      }
    }
  }
`;

export interface RemoveAnnotationOutputType {
  removeAnnotation: {
    ok: boolean;
  };
}

export interface RemoveAnnotationInputType {
  annotationId: string;
}

export const REQUEST_DELETE_ANNOTATION = gql`
  mutation ($annotationId: String!) {
    removeAnnotation(annotationId: $annotationId) {
      ok
    }
  }
`;

export interface RequestDeleteExtractInputType {
  id: string;
}

export interface RequestDeleteExtractOutputType {
  deleteExtract: {
    ok: boolean;
  };
}

export const REQUEST_DELETE_EXTRACT = gql`
  mutation ($id: String!) {
    deleteExtract(id: $id) {
      ok
    }
  }
`;

export interface NewRelationshipInputType {
  relationshipLabelId: string;
  documentId: string;
  corpusId: string;
  sourceIds: string[];
  targetIds: string[];
}

export interface NewRelationshipOutputType {
  addRelationship: {
    ok: boolean;
    relationship: {
      id: string;
      relationshipLabel: AnnotationLabelType;
      sourceAnnotations: {
        edges: [
          {
            node: {
              id: string;
            };
          }
        ];
      };
      targetAnnotations: {
        edges: [
          {
            node: {
              id: string;
            };
          }
        ];
      };
    };
  };
}

export const REQUEST_CREATE_RELATIONSHIP = gql`
  mutation (
    $sourceIds: [String]!
    $targetIds: [String]!
    $relationshipLabelId: String!
    $corpusId: String!
    $documentId: String!
  ) {
    addRelationship(
      sourceIds: $sourceIds
      targetIds: $targetIds
      relationshipLabelId: $relationshipLabelId
      corpusId: $corpusId
      documentId: $documentId
    ) {
      ok
      relationship {
        id
        sourceAnnotations {
          edges {
            node {
              id
            }
          }
        }
        targetAnnotations {
          edges {
            node {
              id
            }
          }
        }
        relationshipLabel {
          id
          icon
          description
          color
          text
          labelType
        }
      }
    }
  }
`;

export interface UpdateRelationshipInput {
  relationshipId: string;
  addSourceIds?: string[];
  addTargetIds?: string[];
  removeSourceIds?: string[];
  removeTargetIds?: string[];
}

export interface UpdateRelationshipOutput {
  updateRelationship: {
    ok: boolean;
    message: string;
    relationship: {
      id: string;
      structural: boolean;
      relationshipLabel: AnnotationLabelType;
      sourceAnnotations: {
        edges: Array<{
          node: {
            id: string;
          };
        }>;
      };
      targetAnnotations: {
        edges: Array<{
          node: {
            id: string;
          };
        }>;
      };
    } | null;
  };
}

export const UPDATE_RELATIONSHIP = gql`
  mutation UpdateRelationship(
    $relationshipId: String!
    $addSourceIds: [String!]
    $addTargetIds: [String!]
    $removeSourceIds: [String!]
    $removeTargetIds: [String!]
  ) {
    updateRelationship(
      relationshipId: $relationshipId
      addSourceIds: $addSourceIds
      addTargetIds: $addTargetIds
      removeSourceIds: $removeSourceIds
      removeTargetIds: $removeTargetIds
    ) {
      ok
      message
      relationship {
        id
        structural
        relationshipLabel {
          id
          text
          color
          icon
          description
        }
        sourceAnnotations {
          edges {
            node {
              id
            }
          }
        }
        targetAnnotations {
          edges {
            node {
              id
            }
          }
        }
      }
    }
  }
`;

export interface RemoveRelationshipOutputType {
  removeRelationship: {
    ok: boolean;
  };
}

export interface RemoveRelationshipInputType {
  relationshipId: string;
}

export const REQUEST_REMOVE_RELATIONSHIP = gql`
  mutation ($relationshipId: String!) {
    removeRelationship(relationshipId: $relationshipId) {
      ok
    }
  }
`;

export interface UpdateRelationOutputType {
  updateRelationships: {
    ok: boolean;
  };
}

export interface UpdateRelationInputType {
  relationships: {
    id: string;
    sourceIds: string[];
    targetIds: string[];
    relationshipLabelId: string;
    corpusId: string;
    documentId: string;
  }[];
}

export const REQUEST_UPDATE_RELATIONS = gql`
  mutation ($relationships: [RelationInputType]) {
    updateRelationships(relationships: $relationships) {
      ok
    }
  }
`;

export interface UpdateAnnotationOutputType {
  updateAnnotation: {
    ok?: boolean;
    message?: string;
  };
}

export interface UpdateAnnotationInputType {
  id: string;
  annotationLabel?: string;
  json?: Record<string, any>;
  page?: number;
  rawText?: string;
  /**
   * URL to open on click for OC_URL annotations. Empty string clears it.
   * Restricted server-side to http(s):// or site-relative paths.
   */
  linkUrl?: string | null;
}

export const REQUEST_UPDATE_ANNOTATION = gql`
  mutation (
    $id: String!
    $annotationLabel: String
    $json: GenericScalar
    $page: Int
    $rawText: String
    $linkUrl: String
  ) {
    updateAnnotation(
      id: $id
      annotationLabel: $annotationLabel
      json: $json
      page: $page
      rawText: $rawText
      linkUrl: $linkUrl
    ) {
      ok
      message
    }
  }
`;

export interface RemoveRelationshipsOutputType {
  removeRelationships: {
    ok: boolean;
  };
}

export interface RemoveRelationshipsInputType {
  relationshipIds: string[];
}

export const REQUEST_REMOVE_RELATIONSHIPS = gql`
  mutation ($relationshipIds: [String]) {
    removeRelationships(relationshipIds: $relationshipIds) {
      ok
    }
  }
`;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ANALYZER-RELATED MUTATIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
export interface RequestDeleteAnalysisOutputType {
  deleteAnalysis: {
    ok: boolean;
    message: string;
  };
}

export interface RequestDeleteAnalysisInputType {
  id: string;
}

export const REQUEST_DELETE_ANALYSIS = gql`
  mutation ($id: String!) {
    deleteAnalysis(id: $id) {
      ok
      message
    }
  }
`;

export interface RequestCreateFieldsetInputType {
  name: string;
  description: string;
}

export interface RequestCreateFieldsetOutputType {
  createFieldset: {
    ok: boolean;
    message: string;
    obj: FieldsetType;
  };
}

export const REQUEST_CREATE_FIELDSET = gql`
  mutation CreateFieldset($name: String!, $description: String!) {
    createFieldset(name: $name, description: $description) {
      ok
      message
      obj {
        id
        name
        description
      }
    }
  }
`;

export interface RequestUpdateFieldsetOutputType {
  updateFieldset: {
    ok: boolean;
    message: string;
    obj: FieldsetType;
  };
}

export interface RequestUpdateFieldsetInputType {
  id: string;
  name?: string;
  description?: string;
}

export const REQUEST_UPDATE_FIELDSET = gql`
  mutation UpdateFieldset($id: ID!, $name: String, $description: String) {
    updateFieldset(id: $id, name: $name, description: $description) {
      message
      ok
      obj {
        id
        name
        description
      }
    }
  }
`;

export interface RequestCreateColumnInputType {
  fieldsetId?: string;
  query: string;
  matchText?: string;
  outputType: string;
  limitToLabel?: string;
  instructions?: string;
  taskName: string;
  name: string;
}

export interface RequestCreateColumnOutputType {
  createColumn: {
    ok: boolean;
    message: string;
    obj: ColumnType;
  };
}

export const REQUEST_CREATE_COLUMN = gql`
  mutation CreateColumn(
    $name: String!
    $fieldsetId: ID!
    $query: String
    $matchText: String
    $outputType: String!
    $limitToLabel: String
    $instructions: String
    $taskName: String
  ) {
    createColumn(
      fieldsetId: $fieldsetId
      query: $query
      matchText: $matchText
      outputType: $outputType
      limitToLabel: $limitToLabel
      instructions: $instructions
      taskName: $taskName
      name: $name
    ) {
      message
      ok
      obj {
        id
        name
        query
        matchText
        outputType
        limitToLabel
        instructions
        taskName
      }
    }
  }
`;

export interface RequestDeleteColumnOutputType {
  deleteColumn: {
    ok: boolean;
    message: string;
    deletedId: string;
  };
}

export interface RequestDeleteColumnInputType {
  id: string;
}

export const REQUEST_DELETE_COLUMN = gql`
  mutation DeleteColumn($id: ID!) {
    deleteColumn(id: $id) {
      ok
      message
      deletedId
    }
  }
`;

export interface RequestAddDocToExtractOutputType {
  addDocsToExtract: {
    ok: boolean;
    message: string;
    objs: DocumentType[];
  };
}

export interface RequestAddDocToExtractInputType {
  documentIds: string[];
  extractId: string;
}

export const REQUEST_ADD_DOC_TO_EXTRACT = gql`
  mutation AddDocToExtract($documentIds: [ID]!, $extractId: ID!) {
    addDocsToExtract(documentIds: $documentIds, extractId: $extractId) {
      ok
      message
      objs {
        __typename
        id
        title
        description
        pageCount
      }
    }
  }
`;

export interface RequestRemoveDocFromExtractOutputType {
  removeDocsFromExtract: {
    ok: boolean;
    message: string;
    idsRemoved: string[];
  };
}

export interface RequestRemoveDocFromExtractInputType {
  documentIdsToRemove: string[];
  extractId: string;
}

export const REQUEST_REMOVE_DOC_FROM_EXTRACT = gql`
  mutation RemoveDocsFromExtract($documentIdsToRemove: [ID]!, $extractId: ID!) {
    removeDocsFromExtract(
      documentIdsToRemove: $documentIdsToRemove
      extractId: $extractId
    ) {
      ok
      message
      idsRemoved
    }
  }
`;

export interface RequestUpdateColumnInputType {
  id: string;
  fieldsetId?: string;
  query?: string;
  matchText?: string;
  outputType?: string;
  limitToLabel?: string;
  instructions?: string;
  taskName?: string;
}

export interface RequestUpdateColumnOutputType {
  updateColumn: {
    ok: boolean;
    message: string;
    obj: ColumnType;
  };
}

export const REQUEST_UPDATE_COLUMN = gql`
  mutation UpdateColumn(
    $id: ID!
    $name: String
    $query: String
    $matchText: String
    $outputType: String
    $limitToLabel: String
    $instructions: String
    $taskName: String
  ) {
    updateColumn(
      id: $id
      name: $name
      query: $query
      matchText: $matchText
      outputType: $outputType
      limitToLabel: $limitToLabel
      instructions: $instructions
      taskName: $taskName
    ) {
      message
      ok
      obj {
        id
        name
        query
        matchText
        outputType
        limitToLabel
        instructions
        taskName
      }
    }
  }
`;

export interface RequestCreateExtractOutputType {
  createExtract: {
    msg: string;
    ok: boolean;
    obj: ExtractType;
  };
}

export interface RequestCreateExtractInputType {
  corpusId?: string;
  name: string;
  fieldsetId?: string;
}

export const REQUEST_CREATE_EXTRACT = gql`
  mutation CreateExtract($corpusId: ID, $name: String!, $fieldsetId: ID) {
    createExtract(corpusId: $corpusId, name: $name, fieldsetId: $fieldsetId) {
      msg
      ok
      obj {
        id
        name
        corpus {
          id
          title
        }
        fieldset {
          id
          name
          inUse
          fullColumnList {
            id
          }
        }
        fullDocumentList {
          id
        }
        creator {
          id
          username
          slug
        }
        created
        started
        finished
        error
        myPermissions
      }
    }
  }
`;

export interface RequestStartExtractOutputType {
  startExtract: {
    message: string;
    ok: boolean;
    obj: ExtractType;
  };
}

export interface RequestStartExtractInputType {
  extractId: string;
}

export const REQUEST_START_EXTRACT = gql`
  mutation StartExtract($extractId: ID!) {
    startExtract(extractId: $extractId) {
      message
      ok
      obj {
        id
        started
        finished
      }
    }
  }
`;

// ----- CreateExtractIteration --------------------------------------------

export type ExtractIterationAxis = "MODEL" | "DOCUMENT_VERSIONS" | "FIELDSET";

export interface RequestCreateExtractIterationInputType {
  sourceExtractId: string;
  axis: ExtractIterationAxis;
  name?: string;
  modelConfig?: Record<string, unknown>;
  columnOverrides?: Record<string, Record<string, unknown>>;
  autoStart?: boolean;
}

export interface RequestCreateExtractIterationOutputType {
  createExtractIteration: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      name: string;
      started: string | null;
      finished: string | null;
      modelConfig: any;
      iterationAxis: ExtractIterationAxis | null;
      parentExtract: { id: string } | null;
    } | null;
  };
}

export const REQUEST_CREATE_EXTRACT_ITERATION = gql`
  mutation CreateExtractIteration(
    $sourceExtractId: ID!
    $axis: String!
    $name: String
    $modelConfig: GenericScalar
    $columnOverrides: GenericScalar
    $autoStart: Boolean
  ) {
    createExtractIteration(
      sourceExtractId: $sourceExtractId
      axis: $axis
      name: $name
      modelConfig: $modelConfig
      columnOverrides: $columnOverrides
      autoStart: $autoStart
    ) {
      ok
      message
      obj {
        id
        name
        started
        finished
        modelConfig
        iterationAxis
        parentExtract {
          id
        }
      }
    }
  }
`;

export interface RequestApproveDatacellInputType {
  datacellId: string;
}

export interface RequestApproveDatacellOutputType {
  approveDatacell: {
    ok: boolean;
    message: string;
    obj: DatacellType;
  };
}

export const REQUEST_APPROVE_DATACELL = gql`
  mutation ApproveDatacell($datacellId: String!) {
    approveDatacell(datacellId: $datacellId) {
      ok
      message
      obj {
        id
        data
        started
        completed
        stacktrace
        correctedData
        column {
          id
        }
        document {
          id
        }
        approvedBy {
          id
          username
        }
        rejectedBy {
          id
          username
        }
      }
    }
  }
`;

export interface RequestRejectDatacellInputType {
  datacellId: string;
}

export interface RequestRejectDatacellOutputType {
  rejectDatacell: {
    ok: boolean;
    message: string;
    obj: DatacellType;
  };
}

export const REQUEST_REJECT_DATACELL = gql`
  mutation RejectDatacell($datacellId: String!) {
    rejectDatacell(datacellId: $datacellId) {
      ok
      message
      obj {
        id
        data
        started
        completed
        stacktrace
        correctedData
        column {
          id
        }
        document {
          id
        }
        approvedBy {
          id
          username
        }
        rejectedBy {
          id
          username
        }
      }
    }
  }
`;

export interface RequestEditDatacellInputType {
  datacellId: string;
  editedData: Record<any, any>;
}

export interface RequestEditDatacellOutputType {
  editDatacell: {
    ok: boolean;
    message: string;
    obj: DatacellType;
  };
}

export const REQUEST_EDIT_DATACELL = gql`
  mutation EditDatacell($datacellId: String!, $editedData: GenericScalar!) {
    editDatacell(datacellId: $datacellId, editedData: $editedData) {
      ok
      message
      obj {
        id
        data
        started
        completed
        stacktrace
        correctedData
        approvedBy {
          id
          username
        }
        rejectedBy {
          id
          username
        }
      }
    }
  }
`;

export interface StartAnalysisInput {
  documentId?: string;
  analyzerId: string;
  corpusId?: string;
  analysisInputData?: Record<string, any>;
}

export interface StartAnalysisOutput {
  startAnalysisOnDoc: {
    ok: boolean;
    message: string;
    obj: AnalysisType;
  };
}

export const START_ANALYSIS = gql`
  mutation StartDocumentAnalysis(
    $documentId: ID
    $analyzerId: ID!
    $corpusId: ID
    $analysisInputData: GenericScalar
  ) {
    startAnalysisOnDoc(
      documentId: $documentId
      analyzerId: $analyzerId
      corpusId: $corpusId
      analysisInputData: $analysisInputData
    ) {
      ok
      message
      obj {
        id
        analysisStarted
        analysisCompleted
        analyzedDocuments {
          edges {
            node {
              id
            }
          }
        }
        receivedCallbackFile
        annotations {
          totalCount
        }
        analyzer {
          id
          analyzerId
          description
          manifest
          labelsetSet {
            totalCount
          }
          hostGremlin {
            id
          }
        }
      }
    }
  }
`;

export interface StartDocumentExtractInput {
  documentId: string;
  fieldsetId: string;
  corpusId?: string;
}

export interface StartDocumentExtractOutput {
  startExtractForDoc: {
    ok: boolean;
    message: string;
    obj: ExtractType;
  };
}

export const START_DOCUMENT_EXTRACT = gql`
  mutation StartDocumentExtract(
    $documentId: ID!
    $fieldsetId: ID!
    $corpusId: ID
  ) {
    startExtractForDoc(
      documentId: $documentId
      fieldsetId: $fieldsetId
      corpusId: $corpusId
    ) {
      ok
      message
      obj {
        id
        name
        started
        corpus {
          id
          title
        }
      }
    }
  }
`;

export interface ApproveAnnotationInput {
  annotationId: string;
  comment?: string;
}

export interface RejectAnnotationInput {
  annotationId: string;
  comment?: string;
}

export interface ApproveAnnotationOutput {
  approveAnnotation: {
    ok: boolean;
    userFeedback: FeedbackType | null;
  };
}

export interface RejectAnnotationOutput {
  rejectAnnotation: {
    ok: boolean;
    userFeedback: FeedbackType | null;
  };
}

// Mutations
export const APPROVE_ANNOTATION = gql`
  mutation ApproveAnnotation($annotationId: ID!, $comment: String) {
    approveAnnotation(annotationId: $annotationId, comment: $comment) {
      ok
      userFeedback {
        id
        approved
        rejected
        comment
        commentedAnnotation {
          id
        }
      }
    }
  }
`;

export const REJECT_ANNOTATION = gql`
  mutation RejectAnnotation($annotationId: ID!, $comment: String) {
    rejectAnnotation(annotationId: $annotationId, comment: $comment) {
      ok
      userFeedback {
        id
        approved
        rejected
        comment
        commentedAnnotation {
          id
        }
      }
    }
  }
`;

export interface RequestUpdateExtractInputType {
  id: string;
  title?: string;
  fieldsetId?: string;
}

export interface RequestUpdateExtractOutputType {
  updateExtract: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
    };
  };
}

export const REQUEST_UPDATE_EXTRACT = gql`
  mutation UpdateExtract($id: ID!, $title: String, $fieldsetId: ID) {
    updateExtract(id: $id, title: $title, fieldsetId: $fieldsetId) {
      ok
      message
      obj {
        id
      }
    }
  }
`;

export const CREATE_CORPUS_ACTION = gql`
  mutation CreateCorpusAction(
    $corpusId: ID!
    $trigger: String!
    $name: String
    $fieldsetId: ID
    $analyzerId: ID
    $agentConfigId: ID
    $taskInstructions: String
    $preAuthorizedTools: [String]
    $createAgentInline: Boolean
    $inlineAgentName: String
    $inlineAgentDescription: String
    $inlineAgentInstructions: String
    $inlineAgentTools: [String]
    $disabled: Boolean
    $runOnAllCorpuses: Boolean
  ) {
    createCorpusAction(
      corpusId: $corpusId
      trigger: $trigger
      name: $name
      fieldsetId: $fieldsetId
      analyzerId: $analyzerId
      agentConfigId: $agentConfigId
      taskInstructions: $taskInstructions
      preAuthorizedTools: $preAuthorizedTools
      createAgentInline: $createAgentInline
      inlineAgentName: $inlineAgentName
      inlineAgentDescription: $inlineAgentDescription
      inlineAgentInstructions: $inlineAgentInstructions
      inlineAgentTools: $inlineAgentTools
      disabled: $disabled
      runOnAllCorpuses: $runOnAllCorpuses
    ) {
      ok
      message
      obj {
        id
        name
        trigger
        disabled
        runOnAllCorpuses
        fieldset {
          id
          name
        }
        analyzer {
          id
          description
        }
        agentConfig {
          id
          name
          description
        }
        taskInstructions
        preAuthorizedTools
      }
    }
  }
`;

export interface CreateCorpusActionInput {
  corpusId: string;
  trigger: "add_document" | "edit_document" | "new_thread" | "new_message";
  name?: string;
  fieldsetId?: string;
  analyzerId?: string;
  agentConfigId?: string;
  taskInstructions?: string;
  preAuthorizedTools?: string[];
  // Inline agent creation parameters
  createAgentInline?: boolean;
  inlineAgentName?: string;
  inlineAgentDescription?: string;
  inlineAgentInstructions?: string;
  inlineAgentTools?: string[];
  disabled?: boolean;
  runOnAllCorpuses?: boolean;
}

export interface CreateCorpusActionOutput {
  createCorpusAction: {
    ok: boolean;
    message: string;
    obj: CorpusActionType | null;
  };
}

export const DELETE_CORPUS_ACTION = gql`
  mutation DeleteCorpusAction($id: String!) {
    deleteCorpusAction(id: $id) {
      ok
      message
    }
  }
`;

export interface DeleteCorpusActionInput {
  id: string;
}

export interface DeleteCorpusActionOutput {
  deleteCorpusAction: {
    ok: boolean;
    message: string;
  };
}

export const UPDATE_CORPUS_ACTION = gql`
  mutation UpdateCorpusAction(
    $id: ID!
    $name: String
    $trigger: String
    $fieldsetId: ID
    $analyzerId: ID
    $agentConfigId: ID
    $taskInstructions: String
    $preAuthorizedTools: [String]
    $disabled: Boolean
    $runOnAllCorpuses: Boolean
  ) {
    updateCorpusAction(
      id: $id
      name: $name
      trigger: $trigger
      fieldsetId: $fieldsetId
      analyzerId: $analyzerId
      agentConfigId: $agentConfigId
      taskInstructions: $taskInstructions
      preAuthorizedTools: $preAuthorizedTools
      disabled: $disabled
      runOnAllCorpuses: $runOnAllCorpuses
    ) {
      ok
      message
      obj {
        id
        name
        trigger
        disabled
        runOnAllCorpuses
        fieldset {
          id
          name
        }
        analyzer {
          id
          description
        }
        agentConfig {
          id
          name
          description
        }
        taskInstructions
        preAuthorizedTools
      }
    }
  }
`;

export interface UpdateCorpusActionInput {
  id: string;
  name?: string;
  trigger?: string;
  fieldsetId?: string;
  analyzerId?: string;
  agentConfigId?: string;
  taskInstructions?: string;
  preAuthorizedTools?: string[];
  disabled?: boolean;
  runOnAllCorpuses?: boolean;
}

export interface UpdateCorpusActionOutput {
  updateCorpusAction: {
    ok: boolean;
    message: string;
    obj: CorpusActionType | null;
  };
}

export const RUN_CORPUS_ACTION = gql`
  mutation RunCorpusAction($corpusActionId: ID!, $documentId: ID!) {
    runCorpusAction(corpusActionId: $corpusActionId, documentId: $documentId) {
      ok
      message
      obj {
        id
        status
        actionType
        trigger
        queuedAt
        corpusAction {
          id
          name
        }
        document {
          id
          title
        }
      }
    }
  }
`;

export interface RunCorpusActionInput {
  corpusActionId: string;
  documentId: string;
}

export interface RunCorpusActionOutput {
  runCorpusAction: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      status: string;
      actionType: string;
      trigger: string;
      queuedAt: string;
      corpusAction: { id: string; name: string };
      document: { id: string; title: string };
    } | null;
  };
}

// `executions` omitted — UI reads summary counts only; re-add if a future caller needs the queued rows.
export const START_CORPUS_ACTION_BATCH_RUN = gql`
  mutation StartCorpusActionBatchRun($corpusActionId: ID!) {
    startCorpusActionBatchRun(corpusActionId: $corpusActionId) {
      ok
      message
      queuedCount
      skippedAlreadyRunCount
      totalActiveDocuments
    }
  }
`;

export interface StartCorpusActionBatchRunInput {
  corpusActionId: string;
}

export interface StartCorpusActionBatchRunOutput {
  startCorpusActionBatchRun: {
    ok: boolean;
    message: string;
    queuedCount: number;
    skippedAlreadyRunCount: number;
    totalActiveDocuments: number;
  };
}

export const ADD_TEMPLATE_TO_CORPUS = gql`
  mutation AddTemplateToCorpus($templateId: ID!, $corpusId: ID!) {
    addTemplateToCorpus(templateId: $templateId, corpusId: $corpusId) {
      ok
      message
      obj {
        id
        name
        trigger
        disabled
        sourceTemplate {
          id
          name
        }
        agentConfig {
          id
          name
          description
        }
        taskInstructions
        preAuthorizedTools
      }
    }
  }
`;

export interface AddTemplateToCorpusInput {
  templateId: string;
  corpusId: string;
}

export interface AddTemplateToCorpusOutput {
  addTemplateToCorpus: {
    ok: boolean;
    message: string;
    obj: CorpusActionType | null;
  };
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// BADGE-RELATED MUTATIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

export const CREATE_BADGE = gql`
  mutation CreateBadge(
    $name: String!
    $description: String!
    $icon: String!
    $badgeType: String!
    $color: String
    $corpusId: ID
    $isAutoAwarded: Boolean
    $criteriaConfig: JSONString
  ) {
    createBadge(
      name: $name
      description: $description
      icon: $icon
      badgeType: $badgeType
      color: $color
      corpusId: $corpusId
      isAutoAwarded: $isAutoAwarded
      criteriaConfig: $criteriaConfig
    ) {
      ok
      message
      badge {
        id
        name
        description
        icon
        badgeType
        color
        isAutoAwarded
        criteriaConfig
        corpus {
          id
          title
        }
      }
    }
  }
`;

export interface CreateBadgeInput {
  name: string;
  description: string;
  icon: string;
  badgeType: "GLOBAL" | "CORPUS";
  color?: string;
  corpusId?: string;
  isAutoAwarded?: boolean;
  criteriaConfig?: any;
}

export interface CreateBadgeOutput {
  createBadge: {
    ok: boolean;
    message: string;
    badge: {
      id: string;
      name: string;
      description: string;
      icon: string;
      badgeType: string;
      color: string;
      isAutoAwarded: boolean;
      criteriaConfig: any;
      corpus?: {
        id: string;
        title: string;
      };
    } | null;
  };
}

export const DELETE_BADGE = gql`
  mutation DeleteBadge($badgeId: ID!) {
    deleteBadge(badgeId: $badgeId) {
      ok
      message
    }
  }
`;

export interface DeleteBadgeInput {
  badgeId: string;
}

export interface DeleteBadgeOutput {
  deleteBadge: {
    ok: boolean;
    message: string;
  };
}

// ============================================================================
// Thread and Message Mutations
// ============================================================================

export const CREATE_THREAD = gql`
  mutation CreateThread(
    $corpusId: String
    $documentId: String
    $title: String!
    $description: String
    $initialMessage: String!
  ) {
    createThread(
      corpusId: $corpusId
      documentId: $documentId
      title: $title
      description: $description
      initialMessage: $initialMessage
    ) {
      ok
      message
      obj {
        id
        title
        description
        chatWithDocument {
          id
          title
          slug
          creator {
            id
            slug
          }
        }
        chatWithCorpus {
          id
          title
          slug
          creator {
            id
            slug
          }
        }
      }
    }
  }
`;

export interface CreateThreadInput {
  corpusId?: string;
  documentId?: string;
  title: string;
  description?: string;
  initialMessage: string;
}

export interface CreateThreadOutput {
  createThread: {
    ok: boolean;
    message: string;
    obj?: {
      id: string;
      title: string;
      description?: string;
    };
  };
}

export const CREATE_THREAD_MESSAGE = gql`
  mutation CreateThreadMessage($conversationId: String!, $content: String!) {
    createThreadMessage(conversationId: $conversationId, content: $content) {
      ok
      message
      obj {
        id
        content
        created
        modified
        creator {
          id
          slug
        }
        conversation {
          id
          title
        }
        upvoteCount
        downvoteCount
        # userVote — backend field not yet exposed in the schema
      }
    }
  }
`;

export interface CreateThreadMessageInput {
  conversationId: string;
  content: string;
}

export interface CreateThreadMessageOutput {
  createThreadMessage: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      content: string;
      created: string;
      modified: string;
      creator: {
        id: string;
        slug: string | null;
      };
      conversation: {
        id: string;
        title: string;
      };
      upvoteCount: number;
      downvoteCount: number;
      userVote?: string;
    } | null;
  };
}

export const REPLY_TO_MESSAGE = gql`
  mutation ReplyToMessage($parentMessageId: String!, $content: String!) {
    replyToMessage(parentMessageId: $parentMessageId, content: $content) {
      ok
      message
      obj {
        id
        content
        created
        modified
        creator {
          id
          slug
        }
        parentMessage {
          id
          content
          creator {
            id
            slug
          }
        }
        conversation {
          id
          title
        }
        upvoteCount
        downvoteCount
        # userVote — backend field not yet exposed in the schema
      }
    }
  }
`;

export interface ReplyToMessageInput {
  parentMessageId: string;
  content: string;
}

export interface ReplyToMessageOutput {
  replyToMessage: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      content: string;
      created: string;
      modified: string;
      creator: {
        id: string;
        slug: string | null;
      };
      parentMessage: {
        id: string;
        content: string;
        creator: {
          id: string;
          slug: string | null;
        };
      } | null;
      conversation: {
        id: string;
        title: string;
      };
      upvoteCount: number;
      downvoteCount: number;
      userVote?: string;
    } | null;
  };
}

export const DELETE_MESSAGE = gql`
  mutation DeleteMessage($messageId: ID!) {
    deleteMessage(messageId: $messageId) {
      ok
      message
    }
  }
`;

export interface DeleteMessageInput {
  messageId: string;
}

export interface DeleteMessageOutput {
  deleteMessage: {
    ok: boolean;
    message: string;
  };
}

/**
 * Update the content of an existing message.
 * Only the message creator or a moderator can edit messages.
 * Part of Issue #686 - Mobile UI for Edit Message Modal
 */
export const UPDATE_MESSAGE = gql`
  mutation UpdateMessage($messageId: ID!, $content: String!) {
    updateMessage(messageId: $messageId, content: $content) {
      ok
      message
      obj {
        id
        content
        modified
      }
    }
  }
`;

export interface UpdateMessageInput {
  messageId: string;
  content: string;
}

export interface UpdateMessageOutput {
  updateMessage: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      content: string;
      modified: string;
    } | null;
  };
}

// ============================================================================
// Voting Mutations
// ============================================================================

/**
 * Upvote a message. Uses the backend vote_message mutation with vote_type="upvote".
 * Returns the updated message with vote counts and current user's vote status.
 */
export const UPVOTE_MESSAGE = gql`
  mutation UpvoteMessage($messageId: String!) {
    voteMessage(messageId: $messageId, voteType: "upvote") {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        userVote
      }
    }
  }
`;

export interface UpvoteMessageInput {
  messageId: string;
}

/** Response shape for vote mutations (upvote uses voteMessage mutation) */
export interface VoteMessageResponse {
  ok: boolean;
  message: string;
  obj: {
    id: string;
    upvoteCount: number;
    downvoteCount: number;
    userVote: string | null;
  } | null;
}

export interface UpvoteMessageOutput {
  voteMessage: VoteMessageResponse;
}

/**
 * Downvote a message. Uses the backend vote_message mutation with vote_type="downvote".
 * Returns the updated message with vote counts and current user's vote status.
 */
export const DOWNVOTE_MESSAGE = gql`
  mutation DownvoteMessage($messageId: String!) {
    voteMessage(messageId: $messageId, voteType: "downvote") {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        userVote
      }
    }
  }
`;

export interface DownvoteMessageInput {
  messageId: string;
}

export interface DownvoteMessageOutput {
  voteMessage: VoteMessageResponse;
}

/**
 * Remove a vote from a message.
 * Returns the updated message with vote counts and current user's vote status (null after removal).
 */
export const REMOVE_VOTE = gql`
  mutation RemoveVote($messageId: String!) {
    removeVote(messageId: $messageId) {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        userVote
      }
    }
  }
`;

export interface RemoveVoteInput {
  messageId: string;
}

export interface RemoveVoteOutput {
  removeVote: VoteMessageResponse;
}

// ============================================================================
// Conversation/Thread Voting Mutations
// ============================================================================

/** Response shape for conversation vote mutations */
export interface VoteConversationResponse {
  ok: boolean;
  message: string;
  obj: {
    id: string;
    upvoteCount: number;
    downvoteCount: number;
    userVote: string | null;
  } | null;
}

/**
 * Upvote a conversation/thread. Uses the backend vote_conversation mutation with vote_type="upvote".
 * Returns the updated conversation with vote counts and current user's vote status.
 */
export const UPVOTE_CONVERSATION = gql`
  mutation UpvoteConversation($conversationId: String!) {
    voteConversation(conversationId: $conversationId, voteType: "upvote") {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        userVote
      }
    }
  }
`;

export interface UpvoteConversationInput {
  conversationId: string;
}

export interface UpvoteConversationOutput {
  voteConversation: VoteConversationResponse;
}

/**
 * Downvote a conversation/thread. Uses the backend vote_conversation mutation with vote_type="downvote".
 * Returns the updated conversation with vote counts and current user's vote status.
 */
export const DOWNVOTE_CONVERSATION = gql`
  mutation DownvoteConversation($conversationId: String!) {
    voteConversation(conversationId: $conversationId, voteType: "downvote") {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        userVote
      }
    }
  }
`;

export interface DownvoteConversationInput {
  conversationId: string;
}

export interface DownvoteConversationOutput {
  voteConversation: VoteConversationResponse;
}

/**
 * Remove a vote from a conversation/thread.
 * Returns the updated conversation with vote counts and current user's vote status (null after removal).
 */
export const REMOVE_CONVERSATION_VOTE = gql`
  mutation RemoveConversationVote($conversationId: String!) {
    removeConversationVote(conversationId: $conversationId) {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        userVote
      }
    }
  }
`;

export interface RemoveConversationVoteInput {
  conversationId: string;
}

export interface RemoveConversationVoteOutput {
  removeConversationVote: VoteConversationResponse;
}

// ============================================================================
// Corpus Voting Mutations (anonymous-friendly)
// ============================================================================
//
// Unlike message / conversation voting these mutations work for anonymous
// viewers too — the backend keys anonymous votes by Django session id and
// blocks self-voting (a corpus creator can't upvote their own corpus).

/** Response shape for corpus vote mutations. */
export interface VoteCorpusResponse {
  ok: boolean;
  message: string;
  obj: {
    id: string;
    upvoteCount: number;
    downvoteCount: number;
    score: number;
    myVote: "UPVOTE" | "DOWNVOTE" | null;
  } | null;
}

/**
 * Upvote a corpus. Uses the backend vote_corpus mutation with vote_type="upvote".
 * Returns the updated corpus with denormalized counts and the viewer's vote.
 */
export const UPVOTE_CORPUS = gql`
  mutation UpvoteCorpus($corpusId: String!) {
    voteCorpus(corpusId: $corpusId, voteType: "upvote") {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        score
        myVote
      }
    }
  }
`;

/**
 * Downvote a corpus.  Backend rules: anonymous voters allowed (gated by
 * corpus visibility), creators cannot vote on their own corpuses.
 */
export const DOWNVOTE_CORPUS = gql`
  mutation DownvoteCorpus($corpusId: String!) {
    voteCorpus(corpusId: $corpusId, voteType: "downvote") {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        score
        myVote
      }
    }
  }
`;

export interface VoteCorpusInput {
  corpusId: string;
}

export interface UpvoteCorpusOutput {
  voteCorpus: VoteCorpusResponse;
}

export interface DownvoteCorpusOutput {
  voteCorpus: VoteCorpusResponse;
}

/**
 * Remove the viewer's vote from a corpus.  Idempotent — removing a
 * non-existent vote returns ok=true with message "No vote to remove".
 */
export const REMOVE_CORPUS_VOTE = gql`
  mutation RemoveCorpusVote($corpusId: String!) {
    removeCorpusVote(corpusId: $corpusId) {
      ok
      message
      obj {
        id
        upvoteCount
        downvoteCount
        score
        myVote
      }
    }
  }
`;

export interface RemoveCorpusVoteInput {
  corpusId: string;
}

export interface RemoveCorpusVoteOutput {
  removeCorpusVote: VoteCorpusResponse;
}

// ============================================================================
// Moderation Mutations
// ============================================================================

export const PIN_THREAD = gql`
  mutation PinThread($conversationId: String!) {
    pinThread(conversationId: $conversationId) {
      ok
      message
      obj {
        id
        isPinned
        pinnedBy {
          id
          username
        }
        pinnedAt
      }
    }
  }
`;

export interface PinThreadInput {
  conversationId: string;
}

export interface PinThreadOutput {
  pinThread: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      isPinned: boolean;
      pinnedBy: {
        id: string;
        username: string;
      } | null;
      pinnedAt: string | null;
    } | null;
  };
}

export const UNPIN_THREAD = gql`
  mutation UnpinThread($conversationId: String!) {
    unpinThread(conversationId: $conversationId) {
      ok
      message
      obj {
        id
        isPinned
        pinnedBy {
          id
          username
        }
        pinnedAt
      }
    }
  }
`;

export interface UnpinThreadInput {
  conversationId: string;
}

export interface UnpinThreadOutput {
  unpinThread: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      isPinned: boolean;
      pinnedBy: {
        id: string;
        username: string;
      } | null;
      pinnedAt: string | null;
    } | null;
  };
}

export const LOCK_THREAD = gql`
  mutation LockThread($conversationId: String!) {
    lockThread(conversationId: $conversationId) {
      ok
      message
      obj {
        id
        isLocked
        lockedBy {
          id
          username
        }
        lockedAt
      }
    }
  }
`;

export interface LockThreadInput {
  conversationId: string;
}

export interface LockThreadOutput {
  lockThread: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      isLocked: boolean;
      lockedBy: {
        id: string;
        username: string;
      } | null;
      lockedAt: string | null;
    } | null;
  };
}

export const UNLOCK_THREAD = gql`
  mutation UnlockThread($conversationId: String!) {
    unlockThread(conversationId: $conversationId) {
      ok
      message
      obj {
        id
        isLocked
        lockedBy {
          id
          username
        }
        lockedAt
      }
    }
  }
`;

export interface UnlockThreadInput {
  conversationId: string;
}

export interface UnlockThreadOutput {
  unlockThread: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      isLocked: boolean;
      lockedBy: {
        id: string;
        username: string;
      } | null;
      lockedAt: string | null;
    } | null;
  };
}

export const DELETE_THREAD = gql`
  mutation DeleteThread($conversationId: ID!) {
    deleteThread(conversationId: $conversationId) {
      ok
      message
    }
  }
`;

export interface DeleteThreadInput {
  conversationId: string;
}

export interface DeleteThreadOutput {
  deleteThread: {
    ok: boolean;
    message: string;
  };
}

export const RESTORE_THREAD = gql`
  mutation RestoreThread($conversationId: ID!) {
    restoreThread(conversationId: $conversationId) {
      ok
      message
    }
  }
`;

export interface RestoreThreadInput {
  conversationId: string;
}

export interface RestoreThreadOutput {
  restoreThread: {
    ok: boolean;
    message: string;
  };
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// DOCUMENT VERSIONING MUTATIONS
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

export const RESTORE_DELETED_DOCUMENT = gql`
  mutation RestoreDeletedDocument($documentId: String!, $corpusId: String!) {
    restoreDeletedDocument(documentId: $documentId, corpusId: $corpusId) {
      ok
      message
      document {
        id
        title
      }
    }
  }
`;

export interface RestoreDeletedDocumentInput {
  documentId: string;
  corpusId: string;
}

export interface RestoreDeletedDocumentOutput {
  restoreDeletedDocument: {
    ok: boolean;
    message: string;
    document: {
      id: string;
      title: string;
    } | null;
  };
}

export const EMPTY_TRASH = gql`
  mutation EmptyTrash($corpusId: String!) {
    emptyTrash(corpusId: $corpusId) {
      ok
      message
      deletedCount
    }
  }
`;

export interface EmptyTrashInput {
  corpusId: string;
}

export interface EmptyTrashOutput {
  emptyTrash: {
    ok: boolean;
    message: string;
    deletedCount: number;
  };
}

/**
 * EMPTY_CORPUS — "empty everything": move EVERY document in the corpus to Trash
 * and remove ALL folders in one step. Documents are soft-deleted (recoverable
 * until the trash is emptied); the folder tree is removed. Requires corpus
 * DELETE permission.
 */
export const EMPTY_CORPUS = gql`
  mutation EmptyCorpus($corpusId: String!) {
    emptyCorpus(corpusId: $corpusId) {
      ok
      message
      trashedCount
    }
  }
`;

export interface EmptyCorpusInput {
  corpusId: string;
}

export interface EmptyCorpusOutput {
  emptyCorpus: {
    ok: boolean;
    message: string;
    trashedCount: number;
  };
}

// ============================================================================
// MODERATION MUTATIONS
// ============================================================================

export const ROLLBACK_MODERATION_ACTION = gql`
  mutation RollbackModerationAction($actionId: ID!, $reason: String) {
    rollbackModerationAction(actionId: $actionId, reason: $reason) {
      ok
      message
      rollbackAction {
        id
        actionType
        created
        moderator {
          id
          username
        }
      }
    }
  }
`;

export interface RollbackModerationActionInput {
  actionId: string;
  reason?: string;
}

export interface RollbackModerationActionOutput {
  rollbackModerationAction: {
    ok: boolean;
    message: string;
    rollbackAction: {
      id: string;
      actionType: string;
      created: string;
      moderator: {
        id: string;
        username: string;
      } | null;
    } | null;
  };
}

// ============================================================================
// DOCUMENT RELATIONSHIP MUTATIONS
// ============================================================================

export interface CreateDocumentRelationshipInputs {
  sourceDocumentId: string;
  targetDocumentId: string;
  relationshipType: string; // "RELATIONSHIP" | "NOTES"
  corpusId: string;
  annotationLabelId?: string;
  data?: Record<string, any>;
}

export interface CreateDocumentRelationshipOutputs {
  createDocumentRelationship: {
    ok: boolean;
    message: string;
    documentRelationship: {
      id: string;
      relationshipType: string;
      data?: Record<string, any>;
      sourceDocument: {
        id: string;
        title: string;
        icon?: string;
      };
      targetDocument: {
        id: string;
        title: string;
        icon?: string;
      };
      annotationLabel?: {
        id: string;
        text: string;
        color: string;
        icon?: string;
      };
      corpus: {
        id: string;
      };
      creator: {
        id: string;
        username: string;
      };
      created: string;
      myPermissions?: string[];
    } | null;
  };
}

export const CREATE_DOCUMENT_RELATIONSHIP = gql`
  mutation CreateDocumentRelationship(
    $sourceDocumentId: String!
    $targetDocumentId: String!
    $relationshipType: String!
    $corpusId: String!
    $annotationLabelId: String
    $data: GenericScalar
  ) {
    createDocumentRelationship(
      sourceDocumentId: $sourceDocumentId
      targetDocumentId: $targetDocumentId
      relationshipType: $relationshipType
      corpusId: $corpusId
      annotationLabelId: $annotationLabelId
      data: $data
    ) {
      ok
      message
      documentRelationship {
        id
        relationshipType
        data
        sourceDocument {
          id
          title
          icon
        }
        targetDocument {
          id
          title
          icon
        }
        annotationLabel {
          id
          text
          color
          icon
        }
        corpus {
          id
        }
        creator {
          id
          slug
          username
        }
        created
        myPermissions
      }
    }
  }
`;

export interface DeleteDocumentRelationshipInputs {
  documentRelationshipId: string;
}

export interface DeleteDocumentRelationshipOutputs {
  deleteDocumentRelationship: {
    ok: boolean;
    message: string;
  };
}

export const DELETE_DOCUMENT_RELATIONSHIP = gql`
  mutation DeleteDocumentRelationship($documentRelationshipId: String!) {
    deleteDocumentRelationship(
      documentRelationshipId: $documentRelationshipId
    ) {
      ok
      message
    }
  }
`;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ZIP IMPORT (folder-structure-preserving) was migrated from GraphQL to
/// multipart REST. See
/// ``frontend/src/utils/importHttp.ts::importZipToCorpusMultipart`` and the
/// ``POST /api/imports/zip-to-corpus/`` endpoint. The base64-over-GraphQL
/// transport crashed Apollo for ZIPs past ~100 MB.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Agent configuration CRUD mutations used by CorpusAgentManagement.
 * Scoped to a single agent at a time (no bulk operations); returns the
 * updated agent so the UI can re-render without a refetch round-trip.
 */
export const CREATE_AGENT_CONFIGURATION = gql`
  mutation CreateAgentConfiguration(
    $name: String!
    $slug: String
    $description: String!
    $systemInstructions: String!
    $availableTools: [String]
    $permissionRequiredTools: [String]
    $badgeConfig: GenericScalar
    $avatarUrl: String
    $scope: String!
    $corpusId: ID
    $isPublic: Boolean
  ) {
    createAgentConfiguration(
      name: $name
      slug: $slug
      description: $description
      systemInstructions: $systemInstructions
      availableTools: $availableTools
      permissionRequiredTools: $permissionRequiredTools
      badgeConfig: $badgeConfig
      avatarUrl: $avatarUrl
      scope: $scope
      corpusId: $corpusId
      isPublic: $isPublic
    ) {
      ok
      message
      agent {
        id
        name
        slug
        description
        badgeConfig
        availableTools
        permissionRequiredTools
        isActive
        isPublic
      }
    }
  }
`;

export const UPDATE_AGENT_CONFIGURATION = gql`
  mutation UpdateAgentConfiguration(
    $agentId: ID!
    $name: String
    $slug: String
    $description: String
    $systemInstructions: String
    $availableTools: [String]
    $permissionRequiredTools: [String]
    $badgeConfig: GenericScalar
    $avatarUrl: String
    $isActive: Boolean
    $isPublic: Boolean
  ) {
    updateAgentConfiguration(
      agentId: $agentId
      name: $name
      slug: $slug
      description: $description
      systemInstructions: $systemInstructions
      availableTools: $availableTools
      permissionRequiredTools: $permissionRequiredTools
      badgeConfig: $badgeConfig
      avatarUrl: $avatarUrl
      isActive: $isActive
      isPublic: $isPublic
    ) {
      ok
      message
      agent {
        id
        name
        slug
        description
        badgeConfig
        availableTools
        permissionRequiredTools
        isActive
        isPublic
      }
    }
  }
`;

export const DELETE_AGENT_CONFIGURATION = gql`
  mutation DeleteAgentConfiguration($agentId: ID!) {
    deleteAgentConfiguration(agentId: $agentId) {
      ok
      message
    }
  }
`;

/* ------------------------------------------------------------------ *
 * Deep-research reports (opencontractserver/research)
 *
 * Start is the explicit (non-chat) kickoff path; the primary trigger is
 * the corpus chat agent's start_deep_research tool. Cancel is cooperative
 * (flips cancel_requested; the agent loop polls it between tool calls).
 * ------------------------------------------------------------------ */

export interface StartResearchReportInput {
  corpusId: string;
  prompt: string;
  title?: string;
  maxSteps?: number;
}
export interface StartResearchReportOutput {
  startResearchReport: {
    ok: boolean;
    message: string;
    obj: ResearchReportType | null;
  };
}
export const START_RESEARCH_REPORT = gql`
  mutation StartResearchReport(
    $corpusId: ID!
    $prompt: String!
    $title: String
    $maxSteps: Int
  ) {
    startResearchReport(
      corpusId: $corpusId
      prompt: $prompt
      title: $title
      maxSteps: $maxSteps
    ) {
      ok
      message
      obj {
        id
        slug
        title
        status
        created
      }
    }
  }
`;

export interface CancelResearchReportInput {
  id: string;
}
export interface CancelResearchReportOutput {
  cancelResearchReport: {
    ok: boolean;
    message: string;
    obj: {
      id: string;
      // Mirror ResearchReportType.status (JobStatus | string) so callers
      // comparing against JobStatus values get type checking on this payload.
      status: JobStatus | string;
      cancelRequested: boolean;
    } | null;
  };
}
export const CANCEL_RESEARCH_REPORT = gql`
  mutation CancelResearchReport($id: ID!) {
    cancelResearchReport(id: $id) {
      ok
      message
      obj {
        id
        status
        cancelRequested
      }
    }
  }
`;
