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

export interface ProjectWithRelations extends Project {
  servers: Array<{ id: string; projectId: string; serverId: string; server: Record<string, unknown> & { id: string; name: string } }>;
}

const PROJECT_INCLUDE = {
  servers: { include: { server: true } },
} as const;

export interface IProjectRepository {
  findAll(ownerId?: string): Promise<ProjectWithRelations[]>;
  findById(id: string): Promise<ProjectWithRelations | null>;
  findByName(name: string): Promise<ProjectWithRelations | null>;
  create(data: { name: string; description: string; prompt?: string; ownerId: string; proxyModel?: string; gated?: boolean; llmProvider?: string; llmModel?: string; serverOverrides?: Record<string, unknown> }): Promise<ProjectWithRelations>;
  update(id: string, data: Record<string, unknown>): Promise<ProjectWithRelations>;
  delete(id: string): Promise<void>;
  setServers(projectId: string, serverIds: string[]): Promise<void>;
  addServer(projectId: string, serverId: string): Promise<void>;
  removeServer(projectId: string, serverId: string): Promise<void>;
}

export class ProjectRepository implements IProjectRepository {
  constructor(private readonly prisma: PrismaClient) {}

  async findAll(ownerId?: string): Promise<ProjectWithRelations[]> {
    const where = ownerId !== undefined ? { ownerId } : {};
    return this.prisma.project.findMany({ where, orderBy: { name: 'asc' }, include: PROJECT_INCLUDE }) as unknown as Promise<ProjectWithRelations[]>;
  }

  async findById(id: string): Promise<ProjectWithRelations | null> {
    return this.prisma.project.findUnique({ where: { id }, include: PROJECT_INCLUDE }) as unknown as Promise<ProjectWithRelations | null>;
  }

  async findByName(name: string): Promise<ProjectWithRelations | null> {
    return this.prisma.project.findUnique({ where: { name }, include: PROJECT_INCLUDE }) as unknown as Promise<ProjectWithRelations | null>;
  }

  async create(data: { name: string; description: string; prompt?: string; ownerId: string; proxyModel?: string; gated?: boolean; llmProvider?: string; llmModel?: string; serverOverrides?: Record<string, unknown> }): Promise<ProjectWithRelations> {
    const createData: Record<string, unknown> = {
      name: data.name,
      description: data.description,
      ownerId: data.ownerId,
    };
    if (data.prompt !== undefined) createData['prompt'] = data.prompt;
    if (data.proxyModel !== undefined) createData['proxyModel'] = data.proxyModel;
    if (data.gated !== undefined) createData['gated'] = data.gated;
    if (data.llmProvider !== undefined) createData['llmProvider'] = data.llmProvider;
    if (data.llmModel !== undefined) createData['llmModel'] = data.llmModel;
    if (data.serverOverrides !== undefined) createData['serverOverrides'] = data.serverOverrides;

    return this.prisma.project.create({
      data: createData as Parameters<PrismaClient['project']['create']>[0]['data'],
      include: PROJECT_INCLUDE,
    }) as unknown as Promise<ProjectWithRelations>;
  }

  async update(id: string, data: Record<string, unknown>): Promise<ProjectWithRelations> {
    return this.prisma.project.update({
      where: { id },
      data,
      include: PROJECT_INCLUDE,
    }) as unknown as Promise<ProjectWithRelations>;
  }

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

  async setServers(projectId: string, serverIds: string[]): Promise<void> {
    await this.prisma.$transaction(async (tx) => {
      await tx.projectServer.deleteMany({ where: { projectId } });
      if (serverIds.length > 0) {
        await tx.projectServer.createMany({
          data: serverIds.map((serverId) => ({ projectId, serverId })),
        });
      }
    });
  }

  async addServer(projectId: string, serverId: string): Promise<void> {
    await this.prisma.projectServer.upsert({
      where: { projectId_serverId: { projectId, serverId } },
      create: { projectId, serverId },
      update: {},
    });
  }

  async removeServer(projectId: string, serverId: string): Promise<void> {
    await this.prisma.projectServer.deleteMany({
      where: { projectId, serverId },
    });
  }
}
