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

export interface CreateSecretBackendInput {
  name: string;
  type: string;
  config?: Record<string, unknown>;
  isDefault?: boolean;
  description?: string;
}

export interface UpdateSecretBackendInput {
  config?: Record<string, unknown>;
  isDefault?: boolean;
  description?: string;
  tokenMeta?: Record<string, unknown>;
}

export interface ISecretBackendRepository {
  findAll(): Promise<SecretBackend[]>;
  findById(id: string): Promise<SecretBackend | null>;
  findByName(name: string): Promise<SecretBackend | null>;
  findDefault(): Promise<SecretBackend | null>;
  create(data: CreateSecretBackendInput): Promise<SecretBackend>;
  update(id: string, data: UpdateSecretBackendInput): Promise<SecretBackend>;
  /**
   * Atomically clear `isDefault` on every row except the one named, then set
   * the given row as default. Used by `setDefault`.
   */
  setAsDefault(id: string): Promise<SecretBackend>;
  delete(id: string): Promise<void>;
  /** Count secrets that still reference this backend — used to guard delete. */
  countReferencingSecrets(backendId: string): Promise<number>;
}

export class SecretBackendRepository implements ISecretBackendRepository {
  constructor(private readonly prisma: PrismaClient) {}

  async findAll(): Promise<SecretBackend[]> {
    return this.prisma.secretBackend.findMany({ orderBy: { name: 'asc' } });
  }

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

  async findByName(name: string): Promise<SecretBackend | null> {
    return this.prisma.secretBackend.findUnique({ where: { name } });
  }

  async findDefault(): Promise<SecretBackend | null> {
    return this.prisma.secretBackend.findFirst({ where: { isDefault: true } });
  }

  async create(data: CreateSecretBackendInput): Promise<SecretBackend> {
    return this.prisma.$transaction(async (tx) => {
      if (data.isDefault === true) {
        await tx.secretBackend.updateMany({ where: { isDefault: true }, data: { isDefault: false } });
      }
      return tx.secretBackend.create({
        data: {
          name: data.name,
          type: data.type,
          config: (data.config ?? {}) as Prisma.InputJsonValue,
          isDefault: data.isDefault ?? false,
          description: data.description ?? '',
        },
      });
    });
  }

  async update(id: string, data: UpdateSecretBackendInput): Promise<SecretBackend> {
    return this.prisma.$transaction(async (tx) => {
      if (data.isDefault === true) {
        await tx.secretBackend.updateMany({
          where: { isDefault: true, NOT: { id } },
          data: { isDefault: false },
        });
      }
      const updateData: Prisma.SecretBackendUpdateInput = {};
      if (data.config !== undefined) updateData.config = data.config as Prisma.InputJsonValue;
      if (data.isDefault !== undefined) updateData.isDefault = data.isDefault;
      if (data.description !== undefined) updateData.description = data.description;
      if (data.tokenMeta !== undefined) updateData.tokenMeta = data.tokenMeta as Prisma.InputJsonValue;
      return tx.secretBackend.update({ where: { id }, data: updateData });
    });
  }

  async setAsDefault(id: string): Promise<SecretBackend> {
    return this.prisma.$transaction(async (tx) => {
      await tx.secretBackend.updateMany({
        where: { isDefault: true, NOT: { id } },
        data: { isDefault: false },
      });
      return tx.secretBackend.update({ where: { id }, data: { isDefault: true } });
    });
  }

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

  async countReferencingSecrets(backendId: string): Promise<number> {
    return this.prisma.secret.count({ where: { backendId } });
  }
}
