import { useAtomSet, useAtomValue } from "@effect/atom-react";
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";

import { connectionsAtom, setSourceCredentialBinding } from "@executor-js/react/api/atoms";
import { useScope, useUserScope } from "@executor-js/react/api/scope-context";
import { connectionWriteKeys, sourceWriteKeys } from "@executor-js/react/api/reactivity-keys";
import { SourceOAuthSignInButton } from "@executor-js/react/plugins/oauth-sign-in";
import { slugifyNamespace } from "@executor-js/react/plugins/source-identity";
import { secretBackedValuesFromConfiguredCredentialBindings } from "@executor-js/react/plugins/credential-bindings";
import { ScopeId } from "@executor-js/sdk/shared";

import { graphqlSourceAtom, graphqlSourceBindingsAtom } from "./atoms";

export default function GraphqlSignInButton(props: { sourceId: string }) {
  const scopeId = useScope();
  const userScopeId = useUserScope();
  const sourceResult = useAtomValue(graphqlSourceAtom(scopeId, props.sourceId));
  const source =
    AsyncResult.isSuccess(sourceResult) && sourceResult.value ? sourceResult.value : null;
  const sourceScope = source ? ScopeId.make(source.scope) : scopeId;
  const bindingsResult = useAtomValue(
    graphqlSourceBindingsAtom(userScopeId, props.sourceId, sourceScope),
  );
  const connectionsResult = useAtomValue(connectionsAtom(userScopeId));
  const setBinding = useAtomSet(setSourceCredentialBinding, { mode: "promise" });

  const oauth2 = source?.auth.kind === "oauth2" ? source.auth : null;
  const bindings = AsyncResult.isSuccess(bindingsResult) ? bindingsResult.value : null;
  const connectionBinding = bindings?.find(
    (binding) => oauth2 !== null && binding.slotKey === oauth2.connectionSlot,
  );
  const boundConnectionId =
    connectionBinding?.value.kind === "connection" ? connectionBinding.value.connectionId : null;
  const connections = AsyncResult.isSuccess(connectionsResult) ? connectionsResult.value : null;
  const isConnected =
    boundConnectionId !== null &&
    connections !== null &&
    connections.some((c: { readonly id: string }) => c.id === boundConnectionId);

  if (!source || !oauth2) return null;
  const namespaceSlug = slugifyNamespace(source.namespace) || "graphql";

  return (
    <SourceOAuthSignInButton
      popupName="graphql-oauth"
      pluginId="graphql"
      namespace={namespaceSlug}
      fallbackNamespace="graphql"
      endpoint={source.endpoint}
      tokenScope={userScopeId}
      connectionId={boundConnectionId}
      sourceLabel={`${source.name.trim() || source.namespace || "GraphQL"} OAuth`}
      headers={secretBackedValuesFromConfiguredCredentialBindings(source.headers, bindings ?? [])}
      queryParams={secretBackedValuesFromConfiguredCredentialBindings(
        source.queryParams,
        bindings ?? [],
      )}
      isConnected={isConnected}
      onConnected={async (connectionId) => {
        await setBinding({
          params: { scopeId: userScopeId },
          payload: {
            scope: userScopeId,
            source: { id: props.sourceId, scope: sourceScope },
            slotKey: oauth2.connectionSlot,
            value: { kind: "connection", connectionId },
          },
          reactivityKeys: [...sourceWriteKeys, ...connectionWriteKeys],
        });
      }}
    />
  );
}
