import { Injectable, NotFoundException, Logger, Optional } from '@nestjs/common';
import { WorkspaceRepository } from './workspace.repository';
import { ContentSafetyService } from '../content-safety/content-safety.service';
import { EventBusService } from '../../common/event-bus/event-bus.service';
import { RedisService } from '../../infrastructure/redis/redis.service';
import { ItemFavoritedEvent } from '../../common/events/item-favorited.event';
import { ItemUnfavoritedEvent } from '../../common/events/item-unfavorited.event';
import { TagCreatedEvent } from '../../common/events/tag-created.event';
import { TagDeletedEvent } from '../../common/events/tag-deleted.event';
import { SearchPerformedEvent } from '../../common/events/search-performed.event';
import {
  RecordRecentDto, AddFavoriteDto, CreateTagDto, UpdateTagDto, AttachTagDto,
} from './dto/workspace.dto';

const DASHBOARD_CACHE_TTL = 300; // 5 min

@Injectable()
export class WorkspaceService {
  private readonly logger = new Logger(WorkspaceService.name);

  constructor(
    private readonly repo: WorkspaceRepository,
    private readonly eventBus: EventBusService,
    @Optional() private readonly redis?: RedisService,
    @Optional() private readonly safety?: ContentSafetyService,
  ) {}

  // ═══ Recent Items ═══

  async getRecentItems(userId: string) {
    return this.repo.findRecentItems(userId);
  }

  async recordRecent(userId: string, dto: RecordRecentDto) {
    await this.repo.upsertRecentItem(userId, dto.targetType, dto.targetId, dto.title, dto.metadata);
    return { ok: true };
  }

  // ═══ Favorites ═══

  async getFavorites(userId: string) {
    return this.repo.findFavorites(userId);
  }

  async addFavorite(userId: string, dto: AddFavoriteDto) {
    const fav = await this.repo.addFavorite(userId, dto.targetType, dto.targetId, dto.title, dto.metadata);

    try { this.eventBus.publish(new ItemFavoritedEvent(userId, fav.id, dto.targetType, dto.targetId)); } catch {}

    return fav;
  }

  async removeFavorite(userId: string, id: string) {
    const fav = await this.repo.findFavoriteById(id);
    if (!fav || fav.userId !== userId) throw new NotFoundException('收藏不存在');

    await this.repo.removeFavorite(userId, id);

    try { this.eventBus.publish(new ItemUnfavoritedEvent(userId, fav.targetType, fav.targetId)); } catch {}

    return { ok: true };
  }

  // ═══ Tags ═══

  async getTags(userId: string) {
    return this.repo.findTags(userId);
  }

  async createTag(userId: string, dto: CreateTagDto) {
    if (this.safety) {
      const check = await this.safety.check(dto.name, { userId, contentType: 'tag' });
      if (!check.safe) throw new NotFoundException('标签名包含敏感词');
    }

    const tag = await this.repo.createTag(userId, dto.name, dto.color);

    try { this.eventBus.publish(new TagCreatedEvent(userId, tag.id, tag.name)); } catch {}

    return tag;
  }

  async updateTag(userId: string, id: string, dto: UpdateTagDto) {
    const tag = await this.repo.findTagById(id);
    if (!tag || tag.userId !== userId) throw new NotFoundException('标签不存在');

    if (dto.name && this.safety) {
      const check = await this.safety.check(dto.name, { userId, contentType: 'tag' });
      if (!check.safe) throw new NotFoundException('标签名包含敏感词');
    }

    return this.repo.updateTag(id, dto);
  }

  async deleteTag(userId: string, id: string) {
    const tag = await this.repo.findTagById(id);
    if (!tag || tag.userId !== userId) throw new NotFoundException('标签不存在');

    await this.repo.deleteTag(id);

    try { this.eventBus.publish(new TagDeletedEvent(userId, tag.id, tag.name)); } catch {}

    return { ok: true };
  }

  // ═══ Tag attach/detach ═══

  async attachTag(userId: string, tagId: string, dto: AttachTagDto) {
    const tag = await this.repo.findTagById(tagId);
    if (!tag || tag.userId !== userId) throw new NotFoundException('标签不存在');

    return this.repo.attachTag(dto.targetId, tagId);
  }

  async detachTag(userId: string, tagId: string, dto: AttachTagDto) {
    const tag = await this.repo.findTagById(tagId);
    if (!tag || tag.userId !== userId) throw new NotFoundException('标签不存在');

    await this.repo.detachTag(dto.targetId, tagId);
    return { ok: true };
  }

  async getItemTags(userId: string, itemId: string) {
    return this.repo.findItemTags(itemId);
  }

  // ═══ Search ═══

  async search(userId: string, q: string, limit = 20, offset = 0) {
    const [kbs, items] = await Promise.all([
      this.repo.searchKnowledgeBases(userId, q, limit, offset),
      this.repo.searchKnowledgeItems(userId, q, limit, offset),
    ]);

    const results = [
      ...kbs.map((k) => ({ targetType: 'knowledge_base', targetId: k.id, title: k.title, snippet: k.description?.slice(0, 200) || '' })),
      ...items.map((i) => ({ targetType: 'knowledge_item', targetId: i.id, title: i.title, snippet: (i.summary || i.content || '').slice(0, 200) })),
    ];

    const total = results.length;
    await this.repo.addSearchHistory(userId, q, total);

    try { this.eventBus.publish(new SearchPerformedEvent(userId, q, total)); } catch {}

    return { results, total, q, limit, offset };
  }

  async getSearchHistory(userId: string) {
    return this.repo.findSearchHistory(userId);
  }

  // ═══ Dashboard ═══

  async getDashboard(userId: string) {
    const cacheKey = `workspace:dashboard:${userId}`;
    if (this.redis) {
      const cached = await this.redis.get(cacheKey);
      if (cached) return JSON.parse(cached);
    }

    const [dueReviews, recentKbs, weeklySeconds] = await Promise.all([
      this.repo.countDueReviews(userId),
      this.repo.findRecentKnowledgeBases(userId),
      this.repo.countWeeklySessions(userId),
    ]);

    const dashboard = {
      dueReviews,
      weeklyLearningMinutes: Math.round(weeklySeconds / 60),
      recentKnowledgeBases: recentKbs.map((k) => ({
        id: k.id, title: k.title, lastStudiedAt: k.lastStudiedAt,
      })),
    };

    if (this.redis) {
      try { await this.redis.set(cacheKey, JSON.stringify(dashboard), DASHBOARD_CACHE_TTL); } catch {}
    }

    return dashboard;
  }
}
