import { HttpModule } from "@nestjs/axios";
import { Logger, Module, OnModuleInit } from "@nestjs/common";
import type { AgentProviderConfig } from "@prisma/client";
import { PrismaModule } from "../../prisma/prisma.module";
import { PrismaService } from "../../prisma/prisma.service";
import { EncryptionService } from "../../security/encryption.service";
import { AgentProviderRegistry } from "../agents/agent-provider.registry";
import { AgentsModule } from "../agents/agents.module";
import { OpenClawProviderFactory } from "./openclaw/openclaw.provider-factory";
import { OpenClawSseBridge } from "./openclaw/openclaw-sse.bridge";

const OPENCLAW_PROVIDER_TYPE = "openclaw";

@Module({
  imports: [
    AgentsModule,
    PrismaModule,
    HttpModule.register({
      timeout: 10000,
      maxRedirects: 5,
    }),
  ],
  providers: [EncryptionService, OpenClawSseBridge, OpenClawProviderFactory],
})
export class ProvidersModule implements OnModuleInit {
  private readonly logger = new Logger(ProvidersModule.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly registry: AgentProviderRegistry,
    private readonly openClawProviderFactory: OpenClawProviderFactory
  ) {}

  async onModuleInit(): Promise<void> {
    const configs = await this.prisma.agentProviderConfig.findMany({
      where: {
        provider: OPENCLAW_PROVIDER_TYPE,
        isActive: true,
      },
      orderBy: [{ createdAt: "asc" }, { id: "asc" }],
    });

    for (const config of configs) {
      await this.registerProvider(config);
    }
  }

  private async registerProvider(config: AgentProviderConfig): Promise<void> {
    const provider = this.openClawProviderFactory.createProvider(config);

    try {
      provider.validateBaseUrl();
    } catch (error) {
      this.logger.warn(
        `Skipping OpenClaw provider ${config.name}: invalid configuration (${this.toErrorMessage(error)})`
      );
      return;
    }

    try {
      provider.validateToken();
    } catch (error) {
      this.logger.error(
        `Skipping OpenClaw provider ${config.name}: token decryption failed (${this.toErrorMessage(error)})`
      );
      return;
    }

    try {
      const available = await provider.isAvailable();
      if (!available) {
        this.logger.warn(
          `Skipping OpenClaw provider ${config.name}: gateway ${config.gatewayUrl} is unreachable`
        );
        return;
      }
    } catch (error) {
      this.logger.warn(
        `Skipping OpenClaw provider ${config.name}: gateway ${config.gatewayUrl} is unreachable (${this.toErrorMessage(error)})`
      );
      return;
    }

    this.registry.registerProvider(provider);
    this.logger.log(`Registered OpenClaw provider ${provider.providerId}`);
  }

  private toErrorMessage(error: unknown): string {
    if (error instanceof Error) {
      return error.message;
    }

    return String(error);
  }
}
