// packages/backend/src/modules/netaclaw/service/memory_admin.ts
import { Provide, Inject } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { NetaClawMemoryEntity } from '../entity/memory.js';
import { MemoryProviderRegistry } from '../memory/registry.js';
import { MemoryEntry } from '../memory/provider.js';

@Provide()
export class MemoryAdminService extends BaseService {
  @Inject()
  registry: MemoryProviderRegistry;

  @InjectEntityModel(NetaClawMemoryEntity)
  memoryEntity: Repository<NetaClawMemoryEntity>;

  async memoryPage(params: { agentName?: string; type?: string; keyWord?: string; page: number; size: number }) {
    if (params.agentName) {
      const { provider, backend } = await this.registry.getProvider(params.agentName);
      const result = await provider.page(params);
      result.list.forEach(e => (e as any)._backend = backend);
      return result;
    }

    const allAgents = await this.registry.getAllAgentsWithMemory();
    const sqliteAgents = allAgents.filter(a => a.backend === 'sqlite');

    const mysqlResult = await this.pageMysqlDirect(params);
    mysqlResult.list.forEach(e => (e as any)._backend = 'mysql');

    const sqliteEntries: (MemoryEntry & { _backend: string })[] = [];
    for (const agent of sqliteAgents) {
      const { provider } = await this.registry.getProvider(agent.name);
      const entries = await provider.list({ agentName: agent.name, limit: 1000 });
      entries.forEach(e => sqliteEntries.push({ ...e, _backend: 'sqlite' } as any));
    }

    const all = [...mysqlResult.list, ...sqliteEntries]
      .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
    const total = mysqlResult.pagination.total + sqliteEntries.length;
    const start = (params.page - 1) * params.size;
    const list = all.slice(start, start + params.size);
    return { list, pagination: { page: params.page, size: params.size, total } };
  }

  private async pageMysqlDirect(params: { type?: string; keyWord?: string; page: number; size: number }) {
    const qb = this.memoryEntity.createQueryBuilder('m');
    if (params.type) qb.andWhere('m.type = :type', { type: params.type });
    if (params.keyWord) {
      qb.andWhere('(m.name LIKE :kw OR m.content LIKE :kw OR m.description LIKE :kw)', { kw: `%${params.keyWord}%` });
    }
    qb.orderBy('m.updateTime', 'DESC');
    const total = await qb.getCount();
    const list = await qb.skip((params.page - 1) * params.size).take(params.size).getMany();
    return {
      list: list.map(e => ({
        id: e.id, agentName: e.agentName, userId: e.userId, type: e.type,
        name: e.name, content: e.content, description: e.description,
        metadata: e.metadata, createdAt: e.createTime, updatedAt: e.updateTime,
      })),
      pagination: { page: params.page, size: params.size, total },
    };
  }

  async memoryInfo(agentName: string, id: number) {
    const { provider, backend } = await this.registry.getProvider(agentName);
    const entry = await provider.getById(id);
    if (entry) (entry as any)._backend = backend;
    return entry;
  }

  async memoryAdd(params: { agentName: string; userId?: string; type: string; name: string; content: string; description?: string; metadata?: Record<string, unknown> }) {
    const { provider } = await this.registry.getProvider(params.agentName);
    return provider.save({
      agentName: params.agentName,
      userId: params.userId ?? 'admin',
      type: params.type,
      name: params.name,
      content: params.content,
      description: params.description ?? '',
      metadata: params.metadata,
    });
  }

  async memoryUpdate(params: { agentName: string; id: number; updatedAt?: string; name?: string; content?: string; description?: string; type?: string; metadata?: Record<string, unknown> }) {
    const { provider } = await this.registry.getProvider(params.agentName);
    if (params.updatedAt) {
      const existing = await provider.getById(params.id);
      const existingTime = new Date(existing.updatedAt).toISOString().slice(0, 19);
      const paramTime = new Date(params.updatedAt).toISOString().slice(0, 19);
      if (existing && existingTime !== paramTime) {
        throw new Error('记忆已被其他操作修改，请刷新后重试');
      }
    }
    return provider.update(params.id, {
      name: params.name,
      content: params.content,
      description: params.description,
      type: params.type,
      metadata: params.metadata,
    });
  }

  async deleteMemories(agentName: string, ids: number[]) {
    const { provider } = await this.registry.getProvider(agentName);
    for (const id of ids) {
      await provider.delete(id);
    }
  }

  async stats(): Promise<Array<{ agentName: string; backend: string; count: number }>> {
    const mysqlStats = await this.memoryEntity
      .createQueryBuilder('m')
      .select('m.agentName', 'agentName')
      .addSelect('COUNT(*)', 'count')
      .groupBy('m.agentName')
      .getRawMany();

    const allAgents = await this.registry.getAllAgentsWithMemory();
    const sqliteAgents = allAgents.filter(a => a.backend === 'sqlite');
    const sqliteStats: Array<{ agentName: string; backend: string; count: number }> = [];
    for (const agent of sqliteAgents) {
      const { provider } = await this.registry.getProvider(agent.name);
      const count = await provider.count({ agentName: agent.name });
      sqliteStats.push({ agentName: agent.name, backend: 'sqlite', count });
    }

    return [
      ...mysqlStats.map((s: any) => ({ agentName: s.agentName, backend: 'mysql', count: Number(s.count) })),
      ...sqliteStats,
    ];
  }
}
