import type { PrismaClient, Prisma, ResourceRevision } from '@prisma/client';

/**
 * Append-only revision log shared by Prompt and Skill (and any future
 * resource type with a `resourceType` discriminator). The repository is
 * intentionally narrow: callers always know which resource they're
 * looking at, so every read takes (resourceType, resourceId) explicitly.
 *
 * `resourceId` is a soft FK — there's no `Prompt`/`Skill` relation here,
 * because revisions need to outlive the resources they describe (audit
 * survives deletion). That means we accept any string and trust the
 * service layer to keep them in sync with real IDs.
 */

export type ResourceType = 'prompt' | 'skill';

export interface CreateRevisionInput {
  resourceType: ResourceType;
  resourceId: string;
  semver: string;
  contentHash: string;
  body: Prisma.InputJsonValue;
  authorUserId?: string;
  authorSessionId?: string;
  note?: string;
}

export interface IResourceRevisionRepository {
  create(data: CreateRevisionInput, tx?: Prisma.TransactionClient): Promise<ResourceRevision>;
  findById(id: string): Promise<ResourceRevision | null>;
  findLatest(resourceType: ResourceType, resourceId: string): Promise<ResourceRevision | null>;
  findHistory(resourceType: ResourceType, resourceId: string, limit?: number): Promise<ResourceRevision[]>;
  findBySemver(resourceType: ResourceType, resourceId: string, semver: string): Promise<ResourceRevision | null>;
  findByContentHash(contentHash: string): Promise<ResourceRevision[]>;
}

export class ResourceRevisionRepository implements IResourceRevisionRepository {
  constructor(private readonly prisma: PrismaClient) {}

  async create(data: CreateRevisionInput, tx?: Prisma.TransactionClient): Promise<ResourceRevision> {
    const client = tx ?? this.prisma;
    return client.resourceRevision.create({ data });
  }

  async findById(id: string): Promise<ResourceRevision | null> {
    return this.prisma.resourceRevision.findUnique({ where: { id } });
  }

  async findLatest(resourceType: ResourceType, resourceId: string): Promise<ResourceRevision | null> {
    return this.prisma.resourceRevision.findFirst({
      where: { resourceType, resourceId },
      orderBy: { createdAt: 'desc' },
    });
  }

  async findHistory(resourceType: ResourceType, resourceId: string, limit = 100): Promise<ResourceRevision[]> {
    return this.prisma.resourceRevision.findMany({
      where: { resourceType, resourceId },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
  }

  async findBySemver(resourceType: ResourceType, resourceId: string, semver: string): Promise<ResourceRevision | null> {
    // Multiple revisions can share a semver if a value was reused (rare,
    // but possible with manual --semver overrides). Return the latest.
    return this.prisma.resourceRevision.findFirst({
      where: { resourceType, resourceId, semver },
      orderBy: { createdAt: 'desc' },
    });
  }

  async findByContentHash(contentHash: string): Promise<ResourceRevision[]> {
    return this.prisma.resourceRevision.findMany({
      where: { contentHash },
      orderBy: { createdAt: 'desc' },
    });
  }
}
