import { z } from "zod";

export const EmbeddingModelSchema = z.string().min(1);
export type EmbeddingModel = string;

/** Maximum number of chunks to embed per embedding API call */
export const EMBEDDING_BATCH_SIZE = 100;

export const SUPPORTED_EMBEDDING_DIMENSIONS = [
  3072, 1536, 1024, 768, 384,
] as const;
export type SupportedEmbeddingDimension =
  (typeof SUPPORTED_EMBEDDING_DIMENSIONS)[number];

/**
 * Supported embedding column sizes. Each entry maps to a dedicated
 * `vector(N)` column and HNSW index in the `kb_chunks` table.
 */
export const EmbeddingDimensionsSchema = z
  .number()
  .int()
  .refine(
    (value) =>
      SUPPORTED_EMBEDDING_DIMENSIONS.includes(
        value as SupportedEmbeddingDimension,
      ),
    {
      message: `Embedding dimensions must be one of: ${SUPPORTED_EMBEDDING_DIMENSIONS.join(", ")}`,
    },
  )
  .meta({
    id: "EmbeddingDimensions",
    enum: [...SUPPORTED_EMBEDDING_DIMENSIONS],
  });

/**
 * Use this alias where the backing storage or application contract is already
 * constrained to supported dimensions. The runtime schema object stays shared,
 * so OpenAPI still emits one reusable component.
 */
export const SupportedEmbeddingDimensionsSchema =
  EmbeddingDimensionsSchema as z.ZodType<SupportedEmbeddingDimension>;

/**
 * Maps a dimension size to its database column name.
 * - 1536 → "embedding" (original column, kept for backward compatibility)
 * - every other supported size → "embedding_<dimensions>" (e.g. "embedding_768")
 */
export function getEmbeddingColumnName(dimensions: number): string {
  if (dimensions === 1536) return "embedding";
  return `embedding_${dimensions}`;
}

/**
 * Display labels for connector types.
 * Used in UI placeholders and titles.
 */
export const CONNECTOR_TYPE_LABELS = {
  jira: "Jira",
  confluence: "Confluence",
  github: "GitHub",
  gitlab: "GitLab",
  notion: "Notion",
  servicenow: "ServiceNow",
  sharepoint: "SharePoint",
  gdrive: "Google Drive",
  dropbox: "Dropbox",
  onedrive: "OneDrive",
  asana: "Asana",
  linear: "Linear",
  outline: "Outline",
  salesforce: "Salesforce",
  web_crawler: "Web Crawler",
  perforce: "Perforce (Helix Core)",
} as const;

export type ConnectorType = keyof typeof CONNECTOR_TYPE_LABELS;

const CONNECTOR_PLACEHOLDER_DEPARTMENTS = [
  "Engineering",
  "Finance",
  "Marketing",
  "Sales",
  "Product",
  "Design",
  "Operations",
  "Support",
];

/**
 * Generate a placeholder connector name like "Marketing Confluence Connector".
 * Picks a random department each call.
 */
export function getConnectorNamePlaceholder(
  connectorType: ConnectorType,
): string {
  const department =
    CONNECTOR_PLACEHOLDER_DEPARTMENTS[
      Math.floor(Math.random() * CONNECTOR_PLACEHOLDER_DEPARTMENTS.length)
    ];
  const label = CONNECTOR_TYPE_LABELS[connectorType] ?? connectorType;
  return `${department} ${label} Connector`;
}

/** Minimum relevance score (0-10) for reranked chunks to be included in results */
export const RERANKER_MIN_RELEVANCE_SCORE = 3;

/**
 * Nomic embedding models require task instruction prefixes in the input text.
 * Documents should use "search_document: " and queries should use "search_query: ".
 * See: https://huggingface.co/nomic-ai/nomic-embed-text-v1.5
 */
type NomicTaskType = "search_document" | "search_query";

export function isNomicModel(model: string): boolean {
  return model.startsWith("nomic") || model.includes("/nomic-embed-text");
}

/**
 * Add the appropriate Nomic task prefix to embedding input text.
 * For non-Nomic models, returns the text unchanged.
 */
export function addNomicTaskPrefix(
  model: string,
  text: string,
  taskType: NomicTaskType,
): string {
  if (!isNomicModel(model)) return text;
  return `${taskType}: ${text}`;
}
