import {
  Entity,
  Column,
  PrimaryColumn,
  ManyToOne,
  JoinColumn,
  CreateDateColumn,
  BeforeInsert,
  Index,
} from 'typeorm';
import { nanoid } from 'nanoid';
import { AIConversation, MessageRole } from './ai-conversation.entity';
import { User } from './user.entity';

@Entity('conversation_embeddings')
@Index(['conversationId'])
@Index(['userId'])
@Index(['createdAt'])
export class ConversationEmbedding {
  @PrimaryColumn({ length: 30 })
  id: string;

  @Column({ name: 'conversation_id', length: 30 })
  conversationId: string;

  @Column({ name: 'user_id', length: 30 })
  userId: string;

  @Column({ name: 'message_index', type: 'int' })
  messageIndex: number;

  @Column({
    name: 'message_role',
    type: 'varchar',
    length: 20,
    enum: MessageRole,
  })
  messageRole: MessageRole;

  @Column({ name: 'message_content', type: 'text' })
  messageContent: string;

  /**
   * Vector embedding (1536 dimensions for OpenAI text-embedding-ada-002 or Azure equivalent)
   * Note: TypeORM doesn't natively support pgvector, so we use string type
   * The actual vector type is handled by PostgreSQL
   */
  @Column({ type: 'text' })
  embedding: string;

  @Column({ type: 'text', array: true, default: [] })
  topics: string[];

  @CreateDateColumn({ name: 'created_at' })
  createdAt: Date;

  // Relations
  @ManyToOne(() => AIConversation, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'conversation_id' })
  conversation: AIConversation;

  @ManyToOne(() => User, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'user_id' })
  user: User;

  @BeforeInsert()
  generateId() {
    if (!this.id) {
      this.id = `emb_${nanoid(16)}`;
    }
  }

  /**
   * Convert vector array to PostgreSQL vector format
   * Input: [0.1, 0.2, 0.3, ...]
   * Output: "[0.1,0.2,0.3,...]"
   */
  static vectorToString(vector: number[]): string {
    return `[${vector.join(',')}]`;
  }

  /**
   * Parse PostgreSQL vector format to array
   * Input: "[0.1,0.2,0.3,...]"
   * Output: [0.1, 0.2, 0.3, ...]
   */
  static stringToVector(str: string): number[] {
    const cleaned = str.replace(/^\[|\]$/g, '');
    return cleaned.split(',').map((v) => parseFloat(v));
  }

  /**
   * Calculate cosine similarity between two vectors
   * Returns value between -1 and 1 (1 = identical, 0 = orthogonal, -1 = opposite)
   */
  static cosineSimilarity(vec1: number[], vec2: number[]): number {
    if (vec1.length !== vec2.length) {
      throw new Error('Vectors must have the same length');
    }

    let dotProduct = 0;
    let magnitude1 = 0;
    let magnitude2 = 0;

    for (let i = 0; i < vec1.length; i++) {
      dotProduct += vec1[i] * vec2[i];
      magnitude1 += vec1[i] * vec1[i];
      magnitude2 += vec2[i] * vec2[i];
    }

    magnitude1 = Math.sqrt(magnitude1);
    magnitude2 = Math.sqrt(magnitude2);

    if (magnitude1 === 0 || magnitude2 === 0) {
      return 0;
    }

    return dotProduct / (magnitude1 * magnitude2);
  }
}
