import { useMemo, useState } from "react";
import { Link } from "@tanstack/react-router";
import { useAtomRefresh, useAtomValue } from "@effect/atom-react";
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
import {
  ToolAddress,
  effectivePolicyFromSorted,
  type ToolPolicyAction,
} from "@executor-js/sdk/shared";

import { policiesOptimisticAtom, toolsAllAtom } from "../api/atoms";
import { usePolicyActions } from "../hooks/use-policy-actions";
import { ToolTree, type ToolSummary } from "../components/tool-tree";
import { ToolDetail, ToolDetailEmpty } from "../components/tool-detail";
import { Button } from "../components/button";
import { Skeleton } from "../components/skeleton";
import { useExecutorDocumentTitle } from "../lib/document-title";
import { ErrorState } from "../components/error-state";
import { isAsyncResultLoading } from "../lib/async-result";
import { useCanCreateWorkspaceConnections } from "../multiplayer/use-admin-nav";

// Dynamic tool policy patterns are derived from the connection-aware address.
// Static tools (for example Executor's own tools) use their address directly.
type ToolRow = {
  readonly address: ToolAddress;
  readonly integration: string;
  readonly name: string;
  readonly description: string;
  readonly requiresApproval?: boolean;
  readonly static?: boolean;
};

const policyId = (tool: ToolRow): string =>
  tool.static ? String(tool.address) : `${tool.integration}.${tool.name}`;

export function ToolsPage() {
  useExecutorDocumentTitle("Tools");
  // Merged across BOTH owners (omit-owner read). The page dedupes to one row per
  // `<integration>.<tool>` policy id, so owner is irrelevant here — the global
  // Tools page is a flat policy tree, not an account-grouped view. Policy writes
  // still target a specific owner (Workspace default; see usePolicyActions).
  const tools = useAtomValue(toolsAllAtom);
  const refreshTools = useAtomRefresh(toolsAllAtom);
  const policies = useAtomValue(policiesOptimisticAtom);
  const policyActions = usePolicyActions("org");
  // Policies here are workspace rules, which the server refuses for non-admin
  // members. Offer the menus only to those who can actually write them.
  const canSetPolicy = useCanCreateWorkspaceConnections();
  const onSetPolicy = canSetPolicy
    ? (pattern: string, action: ToolPolicyAction) => void policyActions.set(pattern, action)
    : undefined;
  const onClearPolicy = canSetPolicy
    ? (pattern: string, policyId?: string) => void policyActions.clear(pattern, policyId)
    : undefined;

  const [selectedToolId, setSelectedToolId] = useState<string | null>(null);

  const policyList = useMemo(
    () => (AsyncResult.isSuccess(policies) ? policies.value : []),
    [policies],
  );

  const sortedPolicies = useMemo(
    () =>
      [...policyList].sort((a, b) => {
        if (a.position < b.position) return -1;
        if (a.position > b.position) return 1;
        return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
      }),
    [policyList],
  );

  // Address + static flag per selection id, so the detail view can fetch the
  // right schema for the selected `<integration>.<tool>` id and its policy
  // badge can write the matching pattern form.
  const selectionById = useMemo(() => {
    const map = new Map<string, { readonly address: ToolAddress; readonly static: boolean }>();
    if (!AsyncResult.isSuccess(tools)) return map;
    for (const t of tools.value as readonly ToolRow[]) {
      const id = policyId(t);
      if (!map.has(id)) map.set(id, { address: t.address, static: t.static === true });
    }
    return map;
  }, [tools]);

  const summaries: ToolSummary[] = useMemo(() => {
    if (!AsyncResult.isSuccess(tools)) return [];
    const seen = new Set<string>();
    const rows: ToolSummary[] = [];
    for (const t of tools.value as readonly ToolRow[]) {
      const id = policyId(t);
      if (seen.has(id)) continue;
      seen.add(id);
      // `id` is the connection-agnostic display id; match policies against the
      // FULL address (`integration.owner.connection.tool`, the address minus its
      // `tools.` prefix) so connection-aware rules resolve.
      const matchId = String(t.address).replace(/^tools\./, "");
      rows.push({
        id,
        name: id,
        description: t.description,
        policy: effectivePolicyFromSorted(matchId, sortedPolicies, t.requiresApproval),
      });
    }
    return rows;
  }, [tools, sortedPolicies]);

  const selectedTool = useMemo(
    () => summaries.find((t) => t.id === selectedToolId) ?? null,
    [summaries, selectedToolId],
  );
  const selection = selectedToolId ? (selectionById.get(selectedToolId) ?? null) : null;
  const selectedAddress = selection?.address ?? null;

  return (
    <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
      {/* Header bar */}
      <div className="flex h-12 shrink-0 items-center justify-between border-b border-border bg-background/95 px-4 backdrop-blur-sm">
        <div className="flex min-w-0 items-center gap-3">
          <h2 className="truncate text-sm font-semibold text-foreground">Tools</h2>
          {AsyncResult.isSuccess(tools) && (
            <span className="hidden text-xs tabular-nums text-muted-foreground sm:block">
              {summaries.length} {summaries.length === 1 ? "tool" : "tools"}
            </span>
          )}
        </div>
        <div className="flex shrink-0 items-center gap-2">
          <Button asChild variant="outline" size="sm">
            <Link to="/{-$orgSlug}/policies">Manage policies</Link>
          </Button>
        </div>
      </div>

      {isAsyncResultLoading(tools) ? (
        <ToolsPageSkeleton />
      ) : (
        AsyncResult.match(tools, {
          onInitial: () => <ToolsPageSkeleton />,
          onFailure: () => (
            <div className="p-6">
              <ErrorState message="Failed to load tools" onRetry={refreshTools} />
            </div>
          ),
          onSuccess: () =>
            summaries.length === 0 ? (
              <div className="flex min-h-0 flex-1 items-center justify-center">
                <div className="text-center">
                  <p className="text-sm font-medium text-foreground/70">No tools registered</p>
                  <p className="mt-1 text-sm text-muted-foreground">
                    Add an integration to start discovering tools.
                  </p>
                </div>
              </div>
            ) : (
              <div className="flex min-h-0 flex-1 overflow-hidden">
                <div className="flex w-72 shrink-0 flex-col border-r border-border/60 lg:w-80 xl:w-[22rem]">
                  <ToolTree
                    tools={summaries}
                    selectedToolId={selectedToolId}
                    onSelect={setSelectedToolId}
                    onSetPolicy={onSetPolicy}
                    onClearPolicy={onClearPolicy}
                    policies={sortedPolicies}
                  />
                </div>
                <div className="flex min-w-0 flex-1 flex-col overflow-hidden">
                  {selectedTool && selectedAddress ? (
                    <ToolDetail
                      address={selectedAddress}
                      toolName={selectedTool.name}
                      staticTool={selection?.static}
                      policy={selectedTool.policy}
                      onSetPolicy={onSetPolicy}
                      onClearPolicy={onClearPolicy}
                    />
                  ) : (
                    <ToolDetailEmpty hasTools={summaries.length > 0} />
                  )}
                </div>
              </div>
            ),
        })
      )}
    </div>
  );
}

function ToolsPageSkeleton() {
  return (
    <div className="flex min-h-0 flex-1 overflow-hidden">
      <div className="flex w-72 shrink-0 flex-col gap-1 border-r border-border/60 p-3 lg:w-80 xl:w-[22rem]">
        <Skeleton className="mb-2 h-8 w-full rounded-md" />
        {Array.from({ length: 8 }).map((_, i) => (
          <div key={i} className="flex items-center gap-2 rounded-md px-2 py-1.5">
            <Skeleton className="size-4 shrink-0 rounded" />
            <Skeleton className="h-3.5" style={{ width: `${55 + ((i * 13) % 35)}%` }} />
          </div>
        ))}
      </div>
      <div className="flex min-w-0 flex-1 flex-col gap-6 overflow-hidden p-6">
        <div className="flex flex-col gap-2">
          <Skeleton className="h-5 w-48" />
          <Skeleton className="h-4 w-80" />
          <Skeleton className="h-4 w-64" />
        </div>
        <div className="flex flex-col gap-3">
          <Skeleton className="h-4 w-24" />
          <Skeleton className="h-20 w-full rounded-md" />
          <Skeleton className="h-4 w-24" />
          <Skeleton className="h-20 w-full rounded-md" />
        </div>
      </div>
    </div>
  );
}
