import type { PrismaClient } from '@prisma/client';
import type {
  IMcpTokenRepository,
  McpTokenFilter,
  McpTokenWithRelations,
  CreateMcpTokenRepoInput,
} from './interfaces.js';

const INCLUDE_RELATIONS = {
  project: { select: { id: true, name: true } },
  owner: { select: { id: true, email: true } },
} as const;

export class McpTokenRepository implements IMcpTokenRepository {
  constructor(private readonly prisma: PrismaClient) {}

  async findAll(filter?: McpTokenFilter): Promise<McpTokenWithRelations[]> {
    const where: Record<string, unknown> = {};
    if (filter?.projectId !== undefined) where['projectId'] = filter.projectId;
    if (filter?.ownerId !== undefined) where['ownerId'] = filter.ownerId;
    if (!filter?.includeRevoked) where['revokedAt'] = null;
    return this.prisma.mcpToken.findMany({
      where,
      include: INCLUDE_RELATIONS,
      orderBy: { createdAt: 'desc' },
    }) as Promise<McpTokenWithRelations[]>;
  }

  async findById(id: string): Promise<McpTokenWithRelations | null> {
    return this.prisma.mcpToken.findUnique({
      where: { id },
      include: INCLUDE_RELATIONS,
    }) as Promise<McpTokenWithRelations | null>;
  }

  async findByHash(tokenHash: string): Promise<McpTokenWithRelations | null> {
    return this.prisma.mcpToken.findUnique({
      where: { tokenHash },
      include: INCLUDE_RELATIONS,
    }) as Promise<McpTokenWithRelations | null>;
  }

  async findByNameAndProject(name: string, projectId: string): Promise<McpTokenWithRelations | null> {
    return this.prisma.mcpToken.findUnique({
      where: { name_projectId: { name, projectId } },
      include: INCLUDE_RELATIONS,
    }) as Promise<McpTokenWithRelations | null>;
  }

  async create(data: CreateMcpTokenRepoInput): Promise<McpTokenWithRelations> {
    return this.prisma.mcpToken.create({
      data: {
        name: data.name,
        projectId: data.projectId,
        ownerId: data.ownerId,
        tokenHash: data.tokenHash,
        tokenPrefix: data.tokenPrefix,
        description: data.description ?? '',
        expiresAt: data.expiresAt ?? null,
      },
      include: INCLUDE_RELATIONS,
    }) as Promise<McpTokenWithRelations>;
  }

  async revoke(id: string): Promise<McpTokenWithRelations> {
    return this.prisma.mcpToken.update({
      where: { id },
      data: { revokedAt: new Date() },
      include: INCLUDE_RELATIONS,
    }) as Promise<McpTokenWithRelations>;
  }

  async touchLastUsed(id: string): Promise<void> {
    await this.prisma.mcpToken.update({
      where: { id },
      data: { lastUsedAt: new Date() },
    });
  }

  async delete(id: string): Promise<void> {
    await this.prisma.mcpToken.delete({ where: { id } });
  }
}
