/**
 * Drizzle Message Repository Adapter
 *
 * Implements MessageRepository interface using Drizzle ORM.
 */

import { eq, and, desc } from 'drizzle-orm';
import { nanoid } from 'nanoid';
import type { DrizzleDatabase } from '../../index.js';
import { messages, type Message } from '../../schema.js';
import type {
  MessageRepository,
  CreateMessageData,
  UpdateMessageData,
  MessageParticipantType,
} from '../message-repository.js';

/**
 * Drizzle adapter for MessageRepository.
 *
 * Uses dependency injection for database instance,
 * enabling isolated test databases.
 */
export class DrizzleMessageRepository implements MessageRepository {
  constructor(private db: DrizzleDatabase) {}

  async create(data: CreateMessageData): Promise<Message> {
    const id = nanoid();
    const now = new Date();

    const [created] = await this.db.insert(messages).values({
      id,
      senderType: data.senderType,
      senderId: data.senderId ?? null,
      recipientType: data.recipientType,
      recipientId: data.recipientId ?? null,
      type: data.type ?? 'info',
      content: data.content,
      requiresResponse: data.requiresResponse ?? false,
      status: data.status ?? 'pending',
      parentMessageId: data.parentMessageId ?? null,
      createdAt: now,
      updatedAt: now,
    }).returning();

    return created;
  }

  async findById(id: string): Promise<Message | null> {
    const result = await this.db
      .select()
      .from(messages)
      .where(eq(messages.id, id))
      .limit(1);

    return result[0] ?? null;
  }

  async findBySender(type: MessageParticipantType, id?: string): Promise<Message[]> {
    if (id) {
      return this.db
        .select()
        .from(messages)
        .where(and(eq(messages.senderType, type), eq(messages.senderId, id)))
        .orderBy(desc(messages.createdAt));
    }

    // For user sender (no id), find where senderType='user' and senderId is null
    return this.db
      .select()
      .from(messages)
      .where(eq(messages.senderType, type))
      .orderBy(desc(messages.createdAt));
  }

  async findByRecipient(type: MessageParticipantType, id?: string): Promise<Message[]> {
    if (id) {
      return this.db
        .select()
        .from(messages)
        .where(and(eq(messages.recipientType, type), eq(messages.recipientId, id)))
        .orderBy(desc(messages.createdAt));
    }

    // For user recipient (no id), find where recipientType='user' and recipientId is null
    return this.db
      .select()
      .from(messages)
      .where(eq(messages.recipientType, type))
      .orderBy(desc(messages.createdAt));
  }

  async findPendingForUser(): Promise<Message[]> {
    return this.db
      .select()
      .from(messages)
      .where(and(eq(messages.recipientType, 'user'), eq(messages.status, 'pending')))
      .orderBy(desc(messages.createdAt));
  }

  async findRequiringResponse(): Promise<Message[]> {
    return this.db
      .select()
      .from(messages)
      .where(and(eq(messages.requiresResponse, true), eq(messages.status, 'pending')))
      .orderBy(desc(messages.createdAt));
  }

  async findReplies(parentMessageId: string): Promise<Message[]> {
    return this.db
      .select()
      .from(messages)
      .where(eq(messages.parentMessageId, parentMessageId))
      .orderBy(desc(messages.createdAt));
  }

  async update(id: string, data: UpdateMessageData): Promise<Message> {
    const [updated] = await this.db
      .update(messages)
      .set({ ...data, updatedAt: new Date() })
      .where(eq(messages.id, id))
      .returning();

    if (!updated) {
      throw new Error(`Message not found: ${id}`);
    }

    return updated;
  }

  async delete(id: string): Promise<void> {
    const [deleted] = await this.db.delete(messages).where(eq(messages.id, id)).returning();

    if (!deleted) {
      throw new Error(`Message not found: ${id}`);
    }
  }
}
