import type { McpServer } from '@prisma/client';
import type { IMcpServerRepository } from '../repositories/interfaces.js';
import type { InstanceService } from './instance.service.js';
import { CreateMcpServerSchema, UpdateMcpServerSchema } from '../validation/mcp-server.schema.js';

export class McpServerService {
  private instanceService: InstanceService | null = null;

  constructor(private readonly repo: IMcpServerRepository) {}

  /** Set after construction to avoid circular dependency. */
  setInstanceService(instanceService: InstanceService): void {
    this.instanceService = instanceService;
  }

  async list(): Promise<McpServer[]> {
    return this.repo.findAll();
  }

  async getById(id: string): Promise<McpServer> {
    const server = await this.repo.findById(id);
    if (server === null) {
      throw new NotFoundError(`Server not found: ${id}`);
    }
    return server;
  }

  async getByName(name: string): Promise<McpServer> {
    const server = await this.repo.findByName(name);
    if (server === null) {
      throw new NotFoundError(`Server not found: ${name}`);
    }
    return server;
  }

  async create(input: unknown): Promise<McpServer> {
    const data = CreateMcpServerSchema.parse(input);

    const existing = await this.repo.findByName(data.name);
    if (existing !== null) {
      throw new ConflictError(`Server already exists: ${data.name}`);
    }

    return this.repo.create(data);
  }

  async update(id: string, input: unknown): Promise<McpServer> {
    const data = UpdateMcpServerSchema.parse(input);

    // Verify exists
    await this.getById(id);

    return this.repo.update(id, data);
  }

  async delete(id: string): Promise<void> {
    // Verify exists
    await this.getById(id);
    // Stop all containers before DB cascade
    if (this.instanceService) {
      await this.instanceService.removeAllForServer(id);
    }
    await this.repo.delete(id);
  }

  // ── Backup/restore helpers ──

  async upsertByName(data: Record<string, unknown>): Promise<McpServer> {
    const name = data['name'] as string;
    const existing = await this.repo.findByName(name);
    if (existing !== null) {
      const { name: _, ...updateFields } = data;
      return this.repo.update(existing.id, updateFields as Parameters<IMcpServerRepository['update']>[1]);
    }
    return this.repo.create(data as Parameters<IMcpServerRepository['create']>[0]);
  }

  async deleteByName(name: string): Promise<void> {
    const existing = await this.repo.findByName(name);
    if (existing === null) return;
    if (this.instanceService) {
      await this.instanceService.removeAllForServer(existing.id);
    }
    await this.repo.delete(existing.id);
  }
}

export class NotFoundError extends Error {
  readonly statusCode = 404;
  constructor(message: string) {
    super(message);
    this.name = 'NotFoundError';
  }
}

export class ConflictError extends Error {
  readonly statusCode = 409;
  constructor(message: string) {
    super(message);
    this.name = 'ConflictError';
  }
}
