import { and, desc, eq } from "drizzle-orm";
import { db } from "../../db";
import { connectors } from "../../db/schema";
import { createTypedHandler } from "./base";
import { connectorContracts } from "../types/connector";
import {
  fetchAndParseOpenApiDocument,
  extractOpenApiEndpoints,
} from "../utils/openapi_utils";
import { DyadError, DyadErrorKind } from "@/errors/dyad_error";

function toConnectorSummary(
  connector: typeof connectors.$inferSelect,
  endpointCount?: number,
) {
  return {
    id: connector.id,
    name: connector.name,
    type: connector.type,
    sourceUrl: connector.sourceUrl,
    specVersion: connector.specVersion,
    description: connector.description,
    endpointCount: endpointCount ?? connector.endpointsJson?.length ?? 0,
    lastSyncedAt: connector.lastSyncedAt,
    createdAt: connector.createdAt,
    updatedAt: connector.updatedAt,
  };
}

function toConnectorDetail(connector: typeof connectors.$inferSelect) {
  return {
    ...toConnectorSummary(connector),
    rawSpec: connector.rawSpec,
    endpoints: connector.endpointsJson ?? [],
  };
}

export function registerConnectorHandlers() {
  createTypedHandler(connectorContracts.listConnectors, async () => {
    const allConnectors = await db.query.connectors.findMany({
      orderBy: [desc(connectors.updatedAt)],
    });

    return {
      connectors: allConnectors.map((connector) =>
        toConnectorSummary(connector),
      ),
    };
  });

  createTypedHandler(connectorContracts.getConnector, async (_, params) => {
    const connector = await db.query.connectors.findFirst({
      where: eq(connectors.id, params.connectorId),
    });

    if (!connector) {
      throw new DyadError("Connector not found", DyadErrorKind.NotFound);
    }

    return toConnectorDetail(connector);
  });

  createTypedHandler(
    connectorContracts.importOpenApiConnector,
    async (_, params) => {
      const imported = await fetchAndParseOpenApiDocument(params.url);
      const now = new Date();

      const connectorName =
        params.name?.trim() && params.name.trim().length > 0
          ? params.name.trim()
          : imported.name;

      const existing = await db.query.connectors.findFirst({
        where: and(
          eq(connectors.sourceUrl, params.url),
          eq(connectors.type, "openapi"),
        ),
      });

      if (existing) {
        await db
          .update(connectors)
          .set({
            name: connectorName,
            description: imported.description,
            specVersion: imported.specVersion,
            rawSpec: imported.rawSpec,
            endpointsJson: imported.endpoints,
            lastSyncedAt: now,
            updatedAt: now,
          })
          .where(eq(connectors.id, existing.id));

        const updated = await db.query.connectors.findFirst({
          where: eq(connectors.id, existing.id),
        });

        if (!updated) {
          throw new DyadError(
            "Connector disappeared during update.",
            DyadErrorKind.Internal,
          );
        }

        return toConnectorDetail(updated);
      }

      const result = await db
        .insert(connectors)
        .values({
          name: connectorName,
          type: "openapi",
          sourceUrl: params.url,
          description: imported.description,
          specVersion: imported.specVersion,
          rawSpec: imported.rawSpec,
          endpointsJson: imported.endpoints,
          lastSyncedAt: now,
          updatedAt: now,
        })
        .returning();

      const created = result[0];
      if (!created) {
        throw new DyadError(
          "Failed to create connector.",
          DyadErrorKind.Internal,
        );
      }

      return toConnectorDetail(created);
    },
  );

  createTypedHandler(connectorContracts.refreshConnector, async (_, params) => {
    const connector = await db.query.connectors.findFirst({
      where: eq(connectors.id, params.connectorId),
    });

    if (!connector) {
      throw new DyadError("Connector not found", DyadErrorKind.NotFound);
    }

    const imported = await fetchAndParseOpenApiDocument(connector.sourceUrl);
    const now = new Date();

    await db
      .update(connectors)
      .set({
        name: connector.name,
        description: imported.description,
        specVersion: imported.specVersion,
        rawSpec: imported.rawSpec,
        endpointsJson: imported.endpoints,
        lastSyncedAt: now,
        updatedAt: now,
      })
      .where(eq(connectors.id, connector.id));

    const refreshed = await db.query.connectors.findFirst({
      where: eq(connectors.id, connector.id),
    });

    if (!refreshed) {
      throw new DyadError(
        "Connector disappeared during refresh.",
        DyadErrorKind.Internal,
      );
    }

    return toConnectorDetail(refreshed);
  });

  createTypedHandler(connectorContracts.deleteConnector, async (_, params) => {
    const connector = await db.query.connectors.findFirst({
      where: eq(connectors.id, params.connectorId),
    });

    if (!connector) {
      throw new DyadError("Connector not found", DyadErrorKind.NotFound);
    }

    await db.delete(connectors).where(eq(connectors.id, params.connectorId));
  });
}

export { extractOpenApiEndpoints };
