import { Injectable, NotFoundException } from "@nestjs/common";
import type { AgentProviderConfig, Prisma } from "@prisma/client";
import { EncryptionService } from "../../security/encryption.service";
import { PrismaService } from "../../prisma/prisma.service";
import { CreateAgentProviderDto } from "./dto/create-agent-provider.dto";
import { UpdateAgentProviderDto } from "./dto/update-agent-provider.dto";

const OPENCLAW_PROVIDER_TYPE = "openclaw";
const OPENCLAW_TOKEN_KEYS = ["apiToken", "token", "bearerToken"] as const;

@Injectable()
export class AgentProvidersService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly encryptionService: EncryptionService
  ) {}

  async list(): Promise<AgentProviderConfig[]> {
    return this.prisma.agentProviderConfig.findMany({
      orderBy: [{ createdAt: "desc" }, { id: "desc" }],
    });
  }

  async getById(id: string): Promise<AgentProviderConfig> {
    const providerConfig = await this.prisma.agentProviderConfig.findUnique({
      where: { id },
    });

    if (!providerConfig) {
      throw new NotFoundException(`Agent provider config with id ${id} not found`);
    }

    return providerConfig;
  }

  async create(dto: CreateAgentProviderDto): Promise<AgentProviderConfig> {
    const credentials = this.sanitizeCredentials(dto.provider, dto.credentials ?? {});

    return this.prisma.agentProviderConfig.create({
      data: {
        workspaceId: dto.workspaceId,
        name: dto.name,
        provider: dto.provider,
        gatewayUrl: dto.gatewayUrl,
        credentials: this.toJsonValue(credentials),
        ...(dto.isActive !== undefined ? { isActive: dto.isActive } : {}),
      },
    });
  }

  async update(id: string, dto: UpdateAgentProviderDto): Promise<AgentProviderConfig> {
    const existingConfig = await this.getById(id);
    const provider = dto.provider ?? existingConfig.provider;

    const data: Prisma.AgentProviderConfigUpdateInput = {
      ...(dto.workspaceId !== undefined ? { workspaceId: dto.workspaceId } : {}),
      ...(dto.name !== undefined ? { name: dto.name } : {}),
      ...(dto.provider !== undefined ? { provider: dto.provider } : {}),
      ...(dto.gatewayUrl !== undefined ? { gatewayUrl: dto.gatewayUrl } : {}),
      ...(dto.isActive !== undefined ? { isActive: dto.isActive } : {}),
      ...(dto.credentials !== undefined
        ? { credentials: this.toJsonValue(this.sanitizeCredentials(provider, dto.credentials)) }
        : {}),
    };

    return this.prisma.agentProviderConfig.update({
      where: { id },
      data,
    });
  }

  async delete(id: string): Promise<AgentProviderConfig> {
    await this.getById(id);

    return this.prisma.agentProviderConfig.delete({
      where: { id },
    });
  }

  private sanitizeCredentials(
    provider: string,
    credentials: Record<string, unknown>
  ): Record<string, unknown> {
    if (provider.toLowerCase() !== OPENCLAW_PROVIDER_TYPE) {
      return credentials;
    }

    const nextCredentials: Record<string, unknown> = { ...credentials };
    for (const key of OPENCLAW_TOKEN_KEYS) {
      const tokenValue = nextCredentials[key];
      if (typeof tokenValue === "string" && tokenValue.length > 0) {
        nextCredentials[key] = this.encryptionService.encryptIfNeeded(tokenValue);
      }
    }

    return nextCredentials;
  }

  private toJsonValue(value: Record<string, unknown>): Prisma.InputJsonValue {
    return value as Prisma.InputJsonValue;
  }
}
