/**
 * Types for vault-core shared utilities
 */

import type Database from 'better-sqlite3';
import type { Statement, Transaction } from 'better-sqlite3';

/**
 * Built-in entity categories
 */
export type DefaultEntityCategory =
  | 'technologies'
  | 'acronyms'
  | 'people'
  | 'projects'
  | 'organizations'
  | 'locations'
  | 'concepts'
  | 'animals'
  | 'media'
  | 'events'
  | 'documents'
  | 'vehicles'
  | 'health'
  | 'finance'
  | 'food'
  | 'hobbies'
  | 'periodical'
  | 'other';

/**
 * Entity category — built-in defaults + user-defined custom categories.
 * Custom categories are configured via FlywheelConfig.custom_categories
 * and populated from frontmatter `type:` fields.
 */
export type EntityCategory = DefaultEntityCategory | (string & Record<never, never>);

/**
 * Entity with optional aliases from frontmatter
 */
export interface EntityWithAliases {
  /** Primary entity name (file stem) */
  name: string;
  /** Relative path to the file within vault */
  path: string;
  /** Aliases from frontmatter (filtered by length/word count) */
  aliases: string[];
  /** Hub score: backlink count for prioritization (set by Flywheel after graph build) */
  hubScore?: number;
  /** Short description: frontmatter description: field or first paragraph, max 200 chars */
  description?: string;
}

/**
 * Entity can be either a simple string (legacy) or full object with aliases
 */
export type Entity = string | EntityWithAliases;

/**
 * Entity with its category type for scoring algorithms
 * Preserves type information that getAllEntities() loses by flattening
 */
export interface EntityWithType {
  entity: EntityWithAliases;
  category: EntityCategory;
}

/** Built-in category keys (excludes _metadata) */
export const DEFAULT_ENTITY_CATEGORIES: DefaultEntityCategory[] = [
  'technologies', 'acronyms', 'people', 'projects', 'organizations',
  'locations', 'concepts', 'animals', 'media', 'events', 'documents',
  'vehicles', 'health', 'finance', 'food', 'hobbies', 'periodical', 'other',
];

/**
 * Entity index structure — built-in categories as typed properties,
 * custom categories accessed dynamically via getIndexCategory().
 */
export interface EntityIndex {
  technologies: Entity[];
  acronyms: Entity[];
  people: Entity[];
  projects: Entity[];
  organizations: Entity[];
  locations: Entity[];
  concepts: Entity[];
  animals: Entity[];
  media: Entity[];
  events: Entity[];
  documents: Entity[];
  vehicles: Entity[];
  health: Entity[];
  finance: Entity[];
  food: Entity[];
  hobbies: Entity[];
  periodical: Entity[];
  other: Entity[];
  /** Dynamic custom categories */
  [customCategory: string]: Entity[] | EntityIndex['_metadata'];
  _metadata: {
    total_entities: number;
    generated_at: string;
    vault_path: string;
    source: string;
    /** Cache version for migration detection */
    version?: number;
  };
}

/** Get entities for a category (handles both built-in and custom) */
export function getIndexCategory(index: EntityIndex, category: string): Entity[] {
  if (category === '_metadata') return [];
  return (index[category] as Entity[]) ?? [];
}

/** Ensure a category array exists on the index */
export function ensureIndexCategory(index: EntityIndex, category: string): void {
  if (category !== '_metadata' && !index[category]) {
    (index as Record<string, unknown>)[category] = [];
  }
}

/**
 * A protected zone in content where wikilinks should not be applied
 */
export interface ProtectedZone {
  start: number;
  end: number;
  type: ProtectedZoneType;
}

/**
 * Types of protected zones
 */
export type ProtectedZoneType =
  | 'frontmatter'
  | 'code_block'
  | 'inline_code'
  | 'wikilink'
  | 'markdown_link'
  | 'url'
  | 'hashtag'
  | 'html_tag'
  | 'obsidian_comment'
  | 'math'
  | 'header'
  | 'obsidian_callout'
  | 'table';

/**
 * Options for entity scanning
 */
export interface ScanOptions {
  /**
   * Folders to exclude from entity scanning (periodic notes, etc.)
   */
  excludeFolders?: string[];

  /**
   * Tech keywords for categorization
   */
  techKeywords?: string[];

  /**
   * Custom entity categories from FlywheelConfig.
   * Keys are frontmatter `type:` values; entities with matching types
   * get that key as their category instead of the default classifier.
   */
  customCategories?: Record<string, { type_boost?: number }>;
}

/**
 * Options for wikilink application
 */
export interface WikilinkOptions {
  /**
   * Only link first occurrence of each entity
   * @default true
   */
  firstOccurrenceOnly?: boolean;

  /**
   * Case-insensitive matching
   * @default true
   */
  caseInsensitive?: boolean;

  /**
   * Entity names (lowercase) already linked by a prior step (e.g. resolveAliasWikilinks).
   * When firstOccurrenceOnly is true, these entities are treated as already seen and
   * will not be linked again by applyWikilinks.
   */
  alreadyLinked?: Set<string>;

  /**
   * Per-alias suppression: (entity, term) pairs that must not be used as
   * search terms, keyed `entityLower||termLower`. Lets feedback suppress one
   * bad alias without poisoning the whole entity.
   */
  suppressedTerms?: Set<string>;
}

/**
 * Result of applying wikilinks
 */
export interface WikilinkResult {
  content: string;
  linksAdded: number;
  linkedEntities: string[];
  /** (entity, matched term) pairs for per-alias feedback tracking */
  linkedTerms?: Array<{ entity: string; matchedTerm: string }>;
  /** Implicit entities detected via pattern matching (non-existent targets) */
  implicitEntities?: string[];
}

/**
 * Configuration for implicit entity detection
 */
export interface ImplicitEntityConfig {
  /**
   * Enable pattern-based detection for non-existent targets
   * @default false
   */
  detectImplicit?: boolean;

  /**
   * Which patterns to use for detection
   * @default ['proper-nouns']
   */
  implicitPatterns?: Array<'proper-nouns' | 'quoted-terms' | 'single-caps' | 'camel-case' | 'acronyms' | 'ticket-refs'>;

  /**
   * Regex patterns to exclude from implicit detection
   * @default ['^The ', '^A ', '^An ', '^This ', '^That ', '^These ', '^Those ']
   */
  excludePatterns?: string[];

  /**
   * Minimum entity length for implicit detection
   * @default 3
   */
  minEntityLength?: number;
}

/**
 * Extended wikilink options with implicit entity support
 */
export interface ExtendedWikilinkOptions extends WikilinkOptions, ImplicitEntityConfig {
  /**
   * Path to current note (for excluding self-links)
   */
  notePath?: string;
}

/**
 * Detected implicit entity with metadata
 */
export interface ImplicitEntityMatch {
  /** The detected entity text */
  text: string;
  /** Start position in content */
  start: number;
  /** End position in content */
  end: number;
  /** Detection method used */
  pattern: 'proper-nouns' | 'quoted-terms' | 'single-caps' | 'camel-case' | 'acronyms' | 'ticket-refs';
}

/**
 * Options for resolving alias-based wikilinks
 */
export interface ResolveAliasOptions {
  /**
   * Case-insensitive matching for aliases
   * @default true
   */
  caseInsensitive?: boolean;
}

// =============================================================================
// State database types (moved from sqlite.ts so queries.ts can type against
// them without importing sqlite.ts — breaks the sqlite ⇄ queries import cycle)
// =============================================================================

/** Search result from FTS5 entity search */
export interface EntitySearchResult {
  id: number;
  name: string;
  nameLower: string;
  path: string;
  category: EntityCategory;
  aliases: string[];
  hubScore: number;
  rank: number;
  description?: string;
}

/** Recency tracking for entities */
export interface RecencyRow {
  entityNameLower: string;
  lastMentionedAt: number;
  mentionCount: number;
}

/** Database state metadata */
export interface StateDbMetadata {
  schemaVersion: number;
  entitiesBuiltAt: string | null;
  entityCount: number;
  notesBuiltAt: string | null;
  noteCount: number;
}

/** State database instance with prepared statements */
export interface StateDb {
  db: Database.Database;
  vaultPath: string;
  dbPath: string;

  // Entity operations
  insertEntity: Statement;
  updateEntity: Statement;
  deleteEntity: Statement;
  getEntityByName: Statement;
  getEntityById: Statement;
  getAllEntities: Statement;
  getEntitiesByCategory: Statement;
  searchEntitiesFts: Statement;
  clearEntities: Statement;

  // Entity alias lookup
  getEntitiesByAlias: Statement;

  // Recency operations
  upsertRecency: Statement;
  getRecency: Statement;
  getAllRecency: Statement;
  clearRecency: Statement;

  // Write state operations
  setWriteState: Statement;
  getWriteState: Statement;
  deleteWriteState: Statement;

  // Flywheel config operations
  setFlywheelConfigStmt: Statement;
  getFlywheelConfigStmt: Statement;
  getAllFlywheelConfigStmt: Statement;
  deleteFlywheelConfigStmt: Statement;

  // Task cache operations
  insertTask: Statement;
  deleteTasksForPath: Statement;
  clearAllTasks: Statement;
  countTasksByStatus: Statement;

  // Metadata
  getMetadataValue: Statement;
  setMetadataValue: Statement;

  // Transactions
  bulkInsertEntities: Transaction<(entities: EntityWithAliases[], category: EntityCategory) => number>;
  replaceAllEntities: Transaction<(index: EntityIndex) => number>;

  // Cleanup
  close: () => void;
}
