import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  HttpStatus,
  Inject,
  NotFoundException,
  Param,
  Post,
  Query,
  UseGuards,
} from '@nestjs/common';
import type { Memory } from '@mosaicstack/memory';
import { MEMORY } from './memory.tokens.js';
import { AuthGuard } from '../auth/auth.guard.js';
import { CurrentUser } from '../auth/current-user.decorator.js';
import { EmbeddingService } from './embedding.service.js';
import type { UpsertPreferenceDto, CreateInsightDto, SearchMemoryDto } from './memory.dto.js';

@Controller('api/memory')
@UseGuards(AuthGuard)
export class MemoryController {
  constructor(
    @Inject(MEMORY) private readonly memory: Memory,
    @Inject(EmbeddingService) private readonly embeddings: EmbeddingService,
  ) {}

  // ─── Preferences ────────────────────────────────────────────────────

  @Get('preferences')
  async listPreferences(@CurrentUser() user: { id: string }, @Query('category') category?: string) {
    if (category) {
      return this.memory.preferences.findByUserAndCategory(
        user.id,
        category as Parameters<typeof this.memory.preferences.findByUserAndCategory>[1],
      );
    }
    return this.memory.preferences.findByUser(user.id);
  }

  @Get('preferences/:key')
  async getPreference(@CurrentUser() user: { id: string }, @Param('key') key: string) {
    const pref = await this.memory.preferences.findByUserAndKey(user.id, key);
    if (!pref) throw new NotFoundException('Preference not found');
    return pref;
  }

  @Post('preferences')
  async upsertPreference(@CurrentUser() user: { id: string }, @Body() dto: UpsertPreferenceDto) {
    return this.memory.preferences.upsert({
      userId: user.id,
      key: dto.key,
      value: dto.value,
      category: dto.category,
      source: dto.source,
    });
  }

  @Delete('preferences/:key')
  @HttpCode(HttpStatus.NO_CONTENT)
  async removePreference(@CurrentUser() user: { id: string }, @Param('key') key: string) {
    const deleted = await this.memory.preferences.remove(user.id, key);
    if (!deleted) throw new NotFoundException('Preference not found');
  }

  // ─── Insights ───────────────────────────────────────────────────────

  @Get('insights')
  async listInsights(@CurrentUser() user: { id: string }, @Query('limit') limit?: string) {
    return this.memory.insights.findByUser(user.id, limit ? Number(limit) : undefined);
  }

  @Get('insights/:id')
  async getInsight(@CurrentUser() user: { id: string }, @Param('id') id: string) {
    const insight = await this.memory.insights.findById(id, user.id);
    if (!insight) throw new NotFoundException('Insight not found');
    return insight;
  }

  @Post('insights')
  async createInsight(@CurrentUser() user: { id: string }, @Body() dto: CreateInsightDto) {
    const embedding = this.embeddings.available
      ? await this.embeddings.embed(dto.content)
      : undefined;

    return this.memory.insights.create({
      userId: user.id,
      content: dto.content,
      source: dto.source,
      category: dto.category,
      metadata: dto.metadata,
      embedding: embedding ?? null,
    });
  }

  @Delete('insights/:id')
  @HttpCode(HttpStatus.NO_CONTENT)
  async removeInsight(@CurrentUser() user: { id: string }, @Param('id') id: string) {
    const deleted = await this.memory.insights.remove(id, user.id);
    if (!deleted) throw new NotFoundException('Insight not found');
  }

  // ─── Search ─────────────────────────────────────────────────────────

  @Post('search')
  async searchMemory(@CurrentUser() user: { id: string }, @Body() dto: SearchMemoryDto) {
    if (!this.embeddings.available) {
      return {
        query: dto.query,
        results: [],
        message: 'Semantic search requires OPENAI_API_KEY for embeddings',
      };
    }

    const queryEmbedding = await this.embeddings.embed(dto.query);
    const results = await this.memory.insights.searchByEmbedding(
      user.id,
      queryEmbedding,
      dto.limit ?? 10,
      dto.maxDistance ?? 0.8,
    );

    return { query: dto.query, results };
  }
}
