"use client";

import { type archestraApiTypes, buildSlackSlashCommands } from "@shared";
import { AlertTriangle, Info } from "lucide-react";
import { useEffect, useState } from "react";
import { CopyButton } from "@/components/copy-button";
import Divider from "@/components/divider";
import { SlackSetupDialog } from "@/components/slack-setup-dialog";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogBody,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { useChatOpsStatus } from "@/lib/chatops/chatops.query";
import { useUpdateSlackChatOpsConfig } from "@/lib/chatops/chatops-config.query";
import config from "@/lib/config/config";
import { useConfig, usePublicBaseUrl } from "@/lib/config/config.query";
import { getFrontendDocsUrl } from "@/lib/docs/docs";
import { useAppName } from "@/lib/hooks/use-app-name";
import { ChannelsSection } from "../_components/channels-section";
import { CollapsibleSetupSection } from "../_components/collapsible-setup-section";
import { CredentialField } from "../_components/credential-field";
import { ExternalDocsLink } from "../_components/external-docs-link";
import { LlmKeySetupStep } from "../_components/llm-key-setup-step";
import { SetupStep } from "../_components/setup-step";
import type { ProviderConfig } from "../_components/types";
import { useTriggerStatuses } from "../_components/use-trigger-statuses";

function useSlackProviderConfig(): ProviderConfig {
  const appName = useAppName();
  return {
    provider: "slack",
    providerLabel: "Slack",
    providerIcon: "/icons/slack.png",
    webhookPath: "/api/webhooks/chatops/slack",
    docsUrl: getFrontendDocsUrl("platform-slack"),
    slashCommand: buildSlackSlashCommands(appName).SELECT_AGENT,
    buildDeepLink: (binding) => {
      if (binding.workspaceId) {
        return `slack://channel?team=${binding.workspaceId}&id=${binding.channelId}`;
      }
      return `slack://channel?id=${binding.channelId}`;
    },
    getDmDeepLink: (providerStatus) => {
      const { botUserId, teamId } = providerStatus.dmInfo ?? {};
      if (!botUserId || !teamId) return null;
      return `slack://user?team=${teamId}&id=${botUserId}`;
    },
  };
}

type SlackConnectionMode = NonNullable<
  NonNullable<
    archestraApiTypes.UpdateSlackChatOpsConfigData["body"]
  >["connectionMode"]
>;

export default function SlackPage() {
  const appName = useAppName();
  const slackProviderConfig = useSlackProviderConfig();
  const publicBaseUrl = usePublicBaseUrl();
  const [slackSetupOpen, setSlackSetupOpen] = useState(false);
  const [ngrokDialogOpen, setNgrokDialogOpen] = useState(false);

  const { data: configData, isLoading: featuresLoading } = useConfig();
  const { data: chatOpsProviders, isLoading: statusLoading } =
    useChatOpsStatus();

  const ngrokDomain = configData?.features.ngrokDomain;
  const slack = chatOpsProviders?.find((p) => p.id === "slack");
  const slackCreds = slack?.credentials;

  const resetMutation = useUpdateSlackChatOpsConfig();

  // Connection mode: use saved value if configured, otherwise default to "socket"
  const savedMode = slackCreds?.connectionMode as
    | SlackConnectionMode
    | undefined;
  const [selectedMode, setSelectedMode] = useState<SlackConnectionMode>(
    savedMode ?? "socket",
  );
  // Sync local state when saved config loads or changes (e.g. after reset)
  useEffect(() => {
    if (savedMode) setSelectedMode(savedMode);
  }, [savedMode]);
  const isSocket = (savedMode ?? selectedMode) === "socket";
  const hasModeChange = savedMode != null && selectedMode !== savedMode;

  const setupDataLoading = featuresLoading || statusLoading;
  const isLocalDev =
    configData?.features.isQuickstart || config.environment === "development";
  const { slack: allStepsCompleted } = useTriggerStatuses();

  return (
    <div className="flex flex-col gap-4">
      <CollapsibleSetupSection
        allStepsCompleted={allStepsCompleted}
        isLoading={setupDataLoading}
        providerLabel="Slack"
        docsUrl={getFrontendDocsUrl("platform-slack")}
      >
        <SetupStep
          title="Choose connection mode"
          description={`How Slack delivers events to ${appName}`}
          done={
            !hasModeChange && (isSocket || (isLocalDev ? !!ngrokDomain : true))
          }
          ctaLabel={
            !isSocket && isLocalDev && !ngrokDomain && !hasModeChange
              ? "Configure ngrok"
              : undefined
          }
          onAction={() => setNgrokDialogOpen(true)}
        >
          <RadioGroup
            value={selectedMode}
            onValueChange={(v: SlackConnectionMode) => setSelectedMode(v)}
            className="flex gap-6"
          >
            {/* biome-ignore lint/a11y/noLabelWithoutControl: RadioGroupItem renders an input */}
            <label className="flex items-start gap-2 cursor-pointer">
              <RadioGroupItem value="socket" className="mt-1" />
              <div className="flex flex-col">
                <span className="text-sm font-medium text-foreground">
                  WebSocket
                </span>
                <span className="text-xs text-muted-foreground">
                  {appName} exchanges WebSocket messages with Slack, no public
                  URL needed
                </span>
              </div>
            </label>
            {/* biome-ignore lint/a11y/noLabelWithoutControl: RadioGroupItem renders an input */}
            <label className="flex items-start gap-2 cursor-pointer">
              <RadioGroupItem value="webhook" className="mt-1" />
              <div className="flex flex-col">
                <span className="text-sm font-medium text-foreground">
                  Webhook
                </span>
                <span className="text-xs text-muted-foreground">
                  Slack makes HTTP requests to {appName}, requires a public URL
                </span>
              </div>
            </label>
          </RadioGroup>
          {selectedMode === "webhook" && !hasModeChange && (
            <div className="flex items-start gap-3 rounded-lg border border-blue-500/30 bg-blue-500/5 px-3 py-2 mt-3">
              <Info className="h-4 w-4 text-blue-500 shrink-0 mt-0.5" />
              <span className="text-muted-foreground text-xs">
                {isLocalDev && ngrokDomain ? (
                  <>
                    Ngrok domain{" "}
                    <code className="bg-muted px-1 py-0.5 rounded">
                      {ngrokDomain}
                    </code>{" "}
                    is configured.
                  </>
                ) : (
                  <>
                    The webhook endpoint{" "}
                    <code className="bg-muted px-1 py-0.5 rounded">
                      POST {`${publicBaseUrl}/api/webhooks/chatops/slack`}
                    </code>{" "}
                    must be publicly accessible so Slack can deliver events to{" "}
                    {appName}.
                    {isLocalDev &&
                      " Configure ngrok or deploy to a public URL."}
                  </>
                )}
              </span>
            </div>
          )}
          {hasModeChange && (
            <div className="mt-3 space-y-3">
              {slack?.configured && (
                <div className="flex items-start gap-3 rounded-lg border border-amber-500/30 bg-amber-500/5 px-3 py-2">
                  <AlertTriangle className="h-4 w-4 text-amber-500 shrink-0 mt-0.5" />
                  <span className="text-muted-foreground text-xs">
                    Changing the connection mode will reset your Slack
                    configuration. You will need to reconfigure Slack with a new
                    app manifest.
                  </span>
                </div>
              )}
              <Button
                size="sm"
                variant={slack?.configured ? "destructive" : "default"}
                disabled={resetMutation.isPending}
                onClick={async () => {
                  await resetMutation.mutateAsync({
                    enabled: false,
                    connectionMode: selectedMode,
                    botToken: "",
                    signingSecret: "",
                    appLevelToken: "",
                    appId: "",
                  });
                }}
              >
                {resetMutation.isPending
                  ? "Saving..."
                  : slack?.configured
                    ? "Reset & switch mode"
                    : "Save"}
              </Button>
            </div>
          )}
        </SetupStep>
        <LlmKeySetupStep />
        <SetupStep
          title="Setup Slack"
          description={`Create a Slack App from manifest and connect it to ${appName}`}
          done={!!slack?.configured}
          ctaLabel="Setup Slack"
          onAction={() => setSlackSetupOpen(true)}
          doneActionLabel="Reconfigure"
          onDoneAction={() => setSlackSetupOpen(true)}
        >
          <div className="flex items-center flex-wrap gap-4">
            <CredentialField
              label="Mode"
              value={isSocket ? "Socket" : "Webhook"}
            />
            <CredentialField label="Bot Token" value={slackCreds?.botToken} />
            {isSocket ? (
              <CredentialField
                label="App-Level Token"
                value={slackCreds?.appLevelToken}
              />
            ) : (
              <CredentialField
                label="Signing Secret"
                value={slackCreds?.signingSecret}
              />
            )}
            <CredentialField label="App ID" value={slackCreds?.appId} />
          </div>
        </SetupStep>
      </CollapsibleSetupSection>

      {allStepsCompleted && (
        <>
          <Divider />
          <ChannelsSection providerConfig={slackProviderConfig} />
        </>
      )}

      <SlackSetupDialog
        open={slackSetupOpen}
        onOpenChange={setSlackSetupOpen}
        connectionMode={savedMode ?? selectedMode}
      />
      <NgrokSetupDialog
        open={ngrokDialogOpen}
        onOpenChange={setNgrokDialogOpen}
      />
    </div>
  );
}

function NgrokSetupDialog({
  open,
  onOpenChange,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const appName = useAppName();
  const [step, setStep] = useState<1 | 2>(1);
  const [authToken, setAuthToken] = useState("");

  const ngrokCommand = `ngrok http --authtoken=${authToken || "<your-ngrok-auth-token>"} 9000`;
  const envCommand =
    "ARCHESTRA_NGROK_DOMAIN=<your-ngrok-domain>.ngrok-free.dev";

  const handleOpenChange = (value: boolean) => {
    onOpenChange(value);
    if (!value) {
      setStep(1);
      setAuthToken("");
    }
  };

  return (
    <Dialog open={open} onOpenChange={handleOpenChange}>
      <DialogContent className="sm:max-w-lg">
        {step === 1 ? (
          <>
            <DialogHeader>
              <DialogTitle>Enter your ngrok auth token</DialogTitle>
              <DialogDescription>
                Get one at{" "}
                <ExternalDocsLink
                  href="https://dashboard.ngrok.com/get-started/your-authtoken"
                  className="inline-flex text-primary"
                >
                  ngrok.com
                </ExternalDocsLink>
              </DialogDescription>
            </DialogHeader>
            <DialogBody className="space-y-4 p-3">
              <Input
                placeholder="ngrok auth token"
                value={authToken}
                onChange={(e) => setAuthToken(e.target.value)}
              />
              <Button
                className="w-full"
                disabled={!authToken.trim()}
                onClick={() => setStep(2)}
              >
                Continue
              </Button>
            </DialogBody>
          </>
        ) : (
          <>
            <DialogHeader>
              <DialogTitle>Run ngrok for Slack webhooks</DialogTitle>
              <DialogDescription>
                Start an ngrok tunnel to make {appName} reachable from Slack.
              </DialogDescription>
            </DialogHeader>
            <DialogBody className="space-y-4 p-3">
              <div className="space-y-2 text-sm">
                <p>1. Start an ngrok tunnel:</p>
                <div className="relative">
                  <pre className="bg-muted rounded-md p-4 text-xs whitespace-pre-wrap break-all">
                    {ngrokCommand}
                  </pre>
                  <div className="absolute top-0 right-0">
                    <CopyButton text={ngrokCommand} />
                  </div>
                </div>
              </div>
              <div className="space-y-2 text-sm">
                <p>
                  2. Set the ngrok domain in your{" "}
                  <code className="bg-muted px-1 py-0.5 rounded text-xs">
                    .env
                  </code>{" "}
                  file:
                </p>
                <div className="relative">
                  <pre className="bg-muted rounded-md p-4 text-xs whitespace-pre-wrap break-all">
                    {envCommand}
                  </pre>
                  <div className="absolute top-0 right-0">
                    <CopyButton text={envCommand} />
                  </div>
                </div>
              </div>
              <p className="text-xs text-muted-foreground">
                Then restart {appName} with{" "}
                <code className="bg-muted px-1 py-0.5 rounded">tilt up</code>
              </p>
            </DialogBody>
          </>
        )}
      </DialogContent>
    </Dialog>
  );
}
