import { randomBytes } from "node:crypto";

import type { PrismaClient } from "@calcom/prisma";
import type { OAuthClientStatus } from "@calcom/prisma/enums";

export class OAuthClientRepository {
  constructor(private readonly prisma: PrismaClient) {}

  async findByClientId(clientId: string) {
    return await this.prisma.oAuthClient.findFirst({
      where: {
        clientId: clientId,
      },
      select: {
        redirectUri: true,
        clientType: true,
        name: true,
        purpose: true,
        logo: true,
        clientId: true,
        isTrusted: true,
        websiteUrl: true,
        rejectionReason: true,
        status: true,
        userId: true,
        createdAt: true,
      },
    });
  }

  async findByClientIdWithSecret(clientId: string) {
    return this.prisma.oAuthClient.findUnique({
      where: { clientId },
      select: {
        clientId: true,
        redirectUri: true,
        clientSecret: true,
        clientType: true,
        status: true,
        userId: true,
      },
    });
  }

  async findByClientIdIncludeUser(clientId: string) {
    return this.prisma.oAuthClient.findUnique({
      where: { clientId },
      select: {
        clientId: true,
        redirectUri: true,
        clientType: true,
        name: true,
        purpose: true,
        logo: true,
        websiteUrl: true,
        rejectionReason: true,
        isTrusted: true,
        status: true,
        userId: true,
        createdAt: true,
        user: {
          select: {
            id: true,
            email: true,
            name: true,
          },
        },
      },
    });
  }

  async findByUserId(userId: number) {
    return this.prisma.oAuthClient.findMany({
      where: { userId },
      select: {
        clientId: true,
        redirectUri: true,
        name: true,
        purpose: true,
        logo: true,
        websiteUrl: true,
        rejectionReason: true,
        clientType: true,
        status: true,
        userId: true,
        createdAt: true,
      },
      orderBy: { createdAt: "desc" },
    });
  }

  async findByUserIdAndStatus(userId: number, status: OAuthClientStatus) {
    return this.prisma.oAuthClient.findMany({
      where: { userId, status },
      orderBy: { createdAt: "desc" },
    });
  }

  async findAll() {
    return this.prisma.oAuthClient.findMany({
      select: {
        clientId: true,
        redirectUri: true,
        name: true,
        purpose: true,
        logo: true,
        websiteUrl: true,
        rejectionReason: true,
        clientType: true,
        status: true,
        userId: true,
        createdAt: true,
        user: {
          select: {
            id: true,
            email: true,
            name: true,
          },
        },
      },
      orderBy: { createdAt: "desc" },
    });
  }

  async findByStatus(status: OAuthClientStatus) {
    return this.prisma.oAuthClient.findMany({
      where: { status },
      select: {
        clientId: true,
        redirectUri: true,
        name: true,
        purpose: true,
        logo: true,
        websiteUrl: true,
        rejectionReason: true,
        clientType: true,
        status: true,
        userId: true,
        createdAt: true,
        user: {
          select: {
            id: true,
            email: true,
            name: true,
          },
        },
      },
      orderBy: { createdAt: "desc" },
    });
  }

  async create(data: {
    name: string;
    purpose: string;
    redirectUri: string;
    clientSecret?: string;
    logo?: string;
    websiteUrl?: string;
    enablePkce?: boolean;
    userId?: number;
    status: OAuthClientStatus;
  }) {
    const { name, purpose, redirectUri, clientSecret, logo, websiteUrl, enablePkce, userId, status } = data;

    const clientId = randomBytes(32).toString("hex");

    const client = await this.prisma.oAuthClient.create({
      data: {
        name,
        purpose,
        redirectUri,
        clientId,
        clientType: enablePkce ? "PUBLIC" : "CONFIDENTIAL",
        logo,
        websiteUrl,
        status,
        clientSecret,
        ...(userId && {
          user: {
            connect: { id: userId },
          },
        }),
      },
    });

    return {
      clientId: client.clientId,
      name: client.name,
      purpose: client.purpose,
      redirectUri: client.redirectUri,
      logo: client.logo,
      clientType: client.clientType,
      clientSecret: client.clientSecret,
      isPkceEnabled: enablePkce,
      status: client.status,
    };
  }

  async updateStatus(clientId: string, status: OAuthClientStatus) {
    return this.prisma.oAuthClient.update({
      where: { clientId },
      data: { status },
    });
  }

  async update(
    clientId: string,
    data: {
      name?: string;
      purpose?: string;
      redirectUri?: string;
      logo?: string;
      websiteUrl?: string;
    }
  ) {
    return this.prisma.oAuthClient.update({
      where: { clientId },
      data,
    });
  }

  async delete(clientId: string) {
    return this.prisma.oAuthClient.delete({
      where: { clientId },
    });
  }
}
