import { useState, useMemo, useCallback, type ReactNode } from "react";
import { RefreshCw } from "lucide-react";
import { useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import {
  displaySuccessToast,
  displaySuccessToastWithLink,
  displayErrorToast,
} from "#/utils/custom-toast-handlers";
import { getApiErrorMessage } from "#/utils/api-error-message";
import {
  useAutomations,
  useToggleAutomation,
  useDeleteAutomation,
  useDispatchAutomation,
  useImportAutomation,
} from "#/hooks/query/use-automations";
import { useAutomationHealth } from "#/hooks/query/use-automation-health";
import { useActiveBackend } from "#/contexts/active-backend-context";
import { useNavigation } from "#/context/navigation-context";
import { SearchInput } from "#/components/features/automations/search-input";
import { AutomationGroup } from "#/components/features/automations/automation-group";
import { AutomationViewToggle } from "#/components/features/automations/automation-view-toggle";
import {
  readStoredAutomationViewMode,
  writeStoredAutomationViewMode,
  type AutomationViewMode,
} from "#/components/features/automations/automation-view-mode";
import { AutomationCardSkeleton } from "#/components/features/automations/automation-card-skeleton";
import { EmptyState } from "#/components/features/automations/empty-state";
import { ErrorState } from "#/components/features/automations/error-state";
import { BackendNotConfigured } from "#/components/features/automations/backend-not-configured";
import { DeleteConfirmationModal } from "#/components/features/automations/delete-confirmation-modal";
import { EditAutomationModal } from "#/components/features/automations/detail/edit-automation-modal";
import { AddAutomationMenu } from "#/components/features/automations/add-automation-menu";
import { AddAutomationModal } from "#/components/features/automations/add-automation-modal";
import { ImportAutomationModal } from "#/components/features/automations/import-automation-modal";
import { RecommendedAutomationsLauncher } from "#/components/features/automations/recommended-automations-launcher";
import { BrandButton } from "#/components/features/settings/brand-button";
import { useTracking } from "#/hooks/use-tracking";
import type { Automation, AutomationSpec } from "#/types/automation";
import {
  getAutomationExportFilename,
  parseAutomationFile,
  serializeAutomation,
} from "#/utils/automation-export";
import {
  automationDetailPath,
  getDashboardSpec,
  getInterfaceCopy,
  hasAutomationInterface,
} from "#/manifests/automation-interface";
import {
  applyDashboardView,
  computeOverviewTile,
  matchesAutomationSearch,
} from "#/manifests/automation-insights";
import { interpolateValues } from "#/manifests/manifest-template";
import type {
  DashboardSortValue,
  DashboardStatusValue,
  DashboardTriggerValue,
} from "#/manifests/types";
import { useAutomationRunSummaries } from "#/hooks/query/use-automation-run-summaries";
import { useAutomationSubPageNav } from "#/components/features/automations/dashboard/use-automation-sub-page-nav";
import { AutomationsDashboardControls } from "#/components/features/automations/dashboard/automations-dashboard-controls";
import { AutomationsFilteredEmptyState } from "#/components/features/automations/dashboard/automations-filtered-empty-state";
import { MANIFEST_ICON_BY_SLUG } from "#/components/features/manifest/manifest-icons";
import { ManifestOverviewTiles } from "#/components/features/manifest/manifest-overview-tiles";
import { ManifestSubpageLayout } from "#/components/features/manifest/manifest-subpage-layout";
import { cn, downloadBlob } from "#/utils/utils";

const PAGE_SIZE = 50;

/**
 * The page renders the interface manifest's copy, so without an admitted
 * manifest there is nothing to render: a 404, which the layout's error
 * boundary renders.
 */
export const clientLoader = () => {
  if (!hasAutomationInterface()) {
    throw new Response(null, { status: 404, statusText: "Not Found" });
  }
  return null;
};

export default function AutomationsList() {
  const { t } = useTranslation("openhands");
  const interfaceCopy = getInterfaceCopy();
  // Admission is stable for the session; memo just keeps one identity.
  const dashboardSpec = useMemo(() => getDashboardSpec(), []);
  const subPageNav = useAutomationSubPageNav();
  // The manifest's dashboard surface is all-or-nothing, so either both are
  // present (dashboard mode) or neither is (today's plain list).
  const dashboard =
    dashboardSpec && subPageNav
      ? { spec: dashboardSpec, nav: subPageNav }
      : null;
  const [searchQuery, setSearchQuery] = useState("");
  const [statusFilter, setStatusFilter] = useState<DashboardStatusValue>("all");
  const [triggerFilter, setTriggerFilter] =
    useState<DashboardTriggerValue>("all");
  const [sortValue, setSortValue] = useState<DashboardSortValue>(
    dashboardSpec?.sort.default ?? "last-run",
  );
  const [viewMode, setViewMode] = useState<AutomationViewMode>(() =>
    readStoredAutomationViewMode(),
  );
  const [limit, setLimit] = useState(PAGE_SIZE);
  const [deleteTarget, setDeleteTarget] = useState<{
    id: string;
    name: string;
  } | null>(null);
  const [editTarget, setEditTarget] = useState<Automation | null>(null);
  const [isAddAutomationOpen, setIsAddAutomationOpen] = useState(false);
  const [importSpec, setImportSpec] = useState<AutomationSpec | null>(null);
  const [isImportOpen, setIsImportOpen] = useState(false);

  const active = useActiveBackend();
  const { navigate } = useNavigation();
  // Edit is a local-backend-only feature in MVP — cloud automations
  // are managed elsewhere and we don't yet surface them here.
  const canEdit = active.backend.kind === "local";

  const {
    data: healthData,
    isLoading: isHealthLoading,
    refetch: refetchHealth,
  } = useAutomationHealth();

  const isBackendHealthy = healthData?.status === "ok";

  // Only fetch automations if the backend is healthy
  const { data, isLoading, isError, refetch } = useAutomations({
    limit,
    offset: 0,
    enabled: isBackendHealthy,
  });
  // One runs query per listed automation — dashboard mode only.
  const runSummaries = useAutomationRunSummaries(data?.automations ?? [], {
    enabled: isBackendHealthy && dashboard !== null,
  });
  const { trackPrebuiltAutomationEnabled, trackAutomationExported } =
    useTracking();
  const toggleMutation = useToggleAutomation();
  const deleteMutation = useDeleteAutomation();
  const dispatchMutation = useDispatchAutomation();
  const importMutation = useImportAutomation();

  const visible = useMemo(() => {
    if (!data?.automations) return [];
    if (!dashboardSpec) {
      return data.automations.filter((a) =>
        matchesAutomationSearch(a, searchQuery),
      );
    }
    return applyDashboardView(
      data.automations,
      {
        search: searchQuery,
        status: statusFilter,
        trigger: triggerFilter,
        sort: sortValue,
      },
      runSummaries,
    );
  }, [
    data?.automations,
    dashboardSpec,
    searchQuery,
    statusFilter,
    triggerFilter,
    sortValue,
    runSummaries,
  ]);

  const activeAutomations = useMemo(
    () => visible.filter((a) => a.enabled),
    [visible],
  );
  const inactive = useMemo(() => visible.filter((a) => !a.enabled), [visible]);

  const handleToggle = (id: string, currentEnabled: boolean) => {
    const willEnable = !currentEnabled;
    toggleMutation.mutate({ id, enabled: willEnable });
    if (willEnable) {
      const automation = data?.automations.find((a) => a.id === id);
      trackPrebuiltAutomationEnabled({
        automationId: id,
        automationName: automation?.name ?? id,
      });
    }
  };

  const handleRunNow = (id: string) => {
    dispatchMutation.mutate(id, {
      onSuccess: () => {
        displaySuccessToast(t(I18nKey.AUTOMATIONS$RUN_NOW_SUCCESS));
      },
      onError: (error) => {
        displayErrorToast(
          getApiErrorMessage(error, t(I18nKey.AUTOMATIONS$RUN_NOW_ERROR)),
        );
      },
    });
  };

  const handleDeleteRequest = (id: string) => {
    const automation = data?.automations.find((a) => a.id === id);
    if (automation) {
      setDeleteTarget({ id, name: automation.name });
    }
  };

  const handleEditRequest = (id: string) => {
    const automation = data?.automations.find((a) => a.id === id);
    if (automation) {
      setEditTarget(automation);
    }
  };

  const handleExport = (automation: Automation) => {
    const contents = `${JSON.stringify(serializeAutomation(automation), null, 2)}\n`;
    downloadBlob(
      new Blob([contents], { type: "application/json" }),
      getAutomationExportFilename(automation),
    );
    trackAutomationExported({ backendKind: active.backend.kind });
  };

  const handleImportFile = async (file: File) => {
    try {
      let parsed: unknown;
      try {
        parsed = JSON.parse(await file.text()) as unknown;
      } catch {
        displayErrorToast(t(I18nKey.AUTOMATIONS$IMPORT_INVALID_JSON));
        return;
      }
      setImportSpec(parseAutomationFile(parsed));
    } catch (error) {
      displayErrorToast(
        error instanceof Error ? error.message : t(I18nKey.ERROR$GENERIC),
      );
    }
  };

  const handleImportConfirm = () => {
    if (!importSpec) return;

    importMutation.mutate(
      { ...importSpec, enabled: false },
      {
        onSuccess: (created) => {
          setIsImportOpen(false);
          setImportSpec(null);
          displaySuccessToastWithLink(
            t(I18nKey.AUTOMATIONS$IMPORT_SUCCESS, { name: created.name }),
            t(I18nKey.AUTOMATIONS$IMPORT_VIEW),
            automationDetailPath(created.id),
          );
        },
        onError: (error) => {
          displayErrorToast(
            getApiErrorMessage(error, t(I18nKey.ERROR$GENERIC)),
          );
        },
      },
    );
  };

  const handleDeleteConfirm = () => {
    if (deleteTarget) {
      deleteMutation.mutate(deleteTarget.id);
      setDeleteTarget(null);
    }
  };

  const handleViewModeChange = useCallback((view: AutomationViewMode) => {
    setViewMode(view);
    writeStoredAutomationViewMode(view);
  }, []);

  // Resets what filters to nothing: search and the dropdowns, never the sort.
  const handleClearFilters = () => {
    setSearchQuery("");
    setStatusFilter("all");
    setTriggerFilter("all");
  };

  const overviewTiles = useMemo(() => {
    if (!dashboardSpec) return [];
    const automations = data?.automations ?? [];
    return dashboardSpec.overview.tiles.map((tile) => {
      const value = computeOverviewTile(tile.metric, automations, runSummaries);
      const template =
        value.isZero && tile.zeroDetail ? tile.zeroDetail : tile.detail;
      return {
        key: tile.metric,
        label: tile.label,
        value: value.display,
        detail: interpolateValues(template, value.placeholderValues),
        Icon: MANIFEST_ICON_BY_SLUG[tile.icon],
      };
    });
  }, [dashboardSpec, data?.automations, runSummaries]);

  const groupInsights = dashboard
    ? { spec: dashboard.spec.insights, byId: runSummaries }
    : undefined;

  // Dashboard mode wraps the page in the manifest's sub-page shell; without a
  // manifest the wrapper — like everything else — is exactly today's.
  const renderShell = (content: ReactNode) =>
    dashboard ? (
      <ManifestSubpageLayout
        heading={dashboard.nav.heading}
        navTestIdBase="automations-navbar"
        items={dashboard.nav.items}
      >
        {content}
      </ManifestSubpageLayout>
    ) : (
      <div className="min-h-full">
        <div className="p-6 max-w-4xl mx-auto">{content}</div>
      </div>
    );

  const hasMore = data ? data.total > data.automations.length : false;
  const hasNoAutomations =
    !isLoading && !isError && data?.automations.length === 0;

  // Show loading state while checking health
  if (isHealthLoading) {
    return renderShell(
      <div>
        <h1 className="text-xl font-medium text-content">
          {interfaceCopy.listTitle}
        </h1>
        <p className="mt-1 text-sm text-muted">{interfaceCopy.listSubtitle}</p>
        <div className="mt-6 flex flex-col gap-3">
          {Array.from({ length: 3 }).map((_, i) => (
            <AutomationCardSkeleton key={`skeleton-${String(i)}`} />
          ))}
        </div>
      </div>,
    );
  }

  // Show backend not configured state if health check failed
  if (!isBackendHealthy) {
    return renderShell(
      <div>
        <h1 className="text-xl font-medium text-content">
          {interfaceCopy.listTitle}
        </h1>
        <p className="mt-1 text-sm text-muted">{interfaceCopy.listSubtitle}</p>
        <BackendNotConfigured onRetry={refetchHealth} />
      </div>,
    );
  }

  return renderShell(
    <>
      {/* Header */}
      <div className="flex items-start justify-between gap-4">
        <div className="min-w-0">
          <h1 className="text-xl font-semibold text-content">
            {interfaceCopy.listTitle}
          </h1>
          <p className="mt-1 text-sm text-muted">
            {interfaceCopy.listSubtitle}
          </p>
        </div>
        <div className="flex shrink-0 flex-wrap justify-end gap-2">
          {canEdit && (
            <BrandButton
              type="button"
              variant="secondary"
              testId="automations-git-sync"
              className="whitespace-nowrap"
              onClick={() => navigate?.("/automations/git-sync")}
              startContent={<RefreshCw className="size-4" aria-hidden />}
            >
              {t(I18nKey.AUTOMATIONS$GIT_SYNC$NAV_BUTTON)}
            </BrandButton>
          )}
          <AddAutomationMenu
            onAdd={() => setIsAddAutomationOpen(true)}
            onImport={() => setIsImportOpen(true)}
          />
        </div>
      </div>

      {/* Overview tiles — dashboard mode only */}
      {dashboard && (
        <ManifestOverviewTiles
          label={dashboard.spec.overview.label}
          tiles={overviewTiles}
        />
      )}

      {/* Search */}
      <div
        className={cn(
          "flex items-stretch gap-2",
          dashboard ? "flex-wrap" : "mt-6",
        )}
      >
        <SearchInput value={searchQuery} onChange={setSearchQuery} />
        {dashboard && (
          <AutomationsDashboardControls
            spec={dashboard.spec}
            status={statusFilter}
            trigger={triggerFilter}
            sort={sortValue}
            onStatusChange={setStatusFilter}
            onTriggerChange={setTriggerFilter}
            onSortChange={setSortValue}
          />
        )}
        <AutomationViewToggle
          view={viewMode}
          onChange={handleViewModeChange}
          disabled={hasNoAutomations}
        />
      </div>

      {/* Content */}
      <div className={cn("flex flex-col gap-6", !dashboard && "mt-6")}>
        {isLoading && (
          <div className="flex flex-col gap-3">
            {Array.from({ length: 3 }).map((_, i) => (
              <AutomationCardSkeleton key={`skeleton-${String(i)}`} />
            ))}
          </div>
        )}

        {isError && !isLoading && <ErrorState onRetry={refetch} />}

        {hasNoAutomations && <EmptyState />}

        {!isLoading &&
          !isError &&
          data &&
          data.automations.length > 0 &&
          (dashboard && visible.length === 0 ? (
            <AutomationsFilteredEmptyState onClear={handleClearFilters} />
          ) : (
            <>
              <AutomationGroup
                title={t(I18nKey.AUTOMATIONS$ACTIVE)}
                count={activeAutomations.length}
                automations={activeAutomations}
                view={viewMode}
                onToggle={handleToggle}
                onRunNow={handleRunNow}
                runPendingId={
                  dispatchMutation.isPending
                    ? (dispatchMutation.variables ?? null)
                    : null
                }
                onDelete={handleDeleteRequest}
                onExport={handleExport}
                onEdit={canEdit ? handleEditRequest : undefined}
                insights={groupInsights}
              />
              <AutomationGroup
                title={t(I18nKey.AUTOMATIONS$INACTIVE)}
                count={inactive.length}
                automations={inactive}
                view={viewMode}
                onToggle={handleToggle}
                onRunNow={handleRunNow}
                runPendingId={
                  dispatchMutation.isPending
                    ? (dispatchMutation.variables ?? null)
                    : null
                }
                onDelete={handleDeleteRequest}
                onExport={handleExport}
                onEdit={canEdit ? handleEditRequest : undefined}
                insights={groupInsights}
              />

              {hasMore && (
                <button
                  type="button"
                  onClick={() => setLimit((prev) => prev + PAGE_SIZE)}
                  className="self-center rounded-lg border border-[var(--oh-border)] px-6 py-2 text-sm text-white hover:bg-surface-raised"
                >
                  {t(I18nKey.AUTOMATIONS$LOAD_MORE)}
                </button>
              )}
            </>
          ))}
      </div>

      {/* The launcher lives on the templates sub-page in dashboard mode */}
      {!dashboard && (
        <div className="mt-6">
          <RecommendedAutomationsLauncher query={searchQuery} />
        </div>
      )}

      {/* Delete confirmation modal */}
      <DeleteConfirmationModal
        automationName={deleteTarget?.name ?? ""}
        isOpen={deleteTarget !== null}
        onConfirm={handleDeleteConfirm}
        onCancel={() => setDeleteTarget(null)}
      />

      {/* Edit modal — local backends only */}
      {editTarget && (
        <EditAutomationModal
          automation={editTarget}
          isOpen={editTarget !== null}
          onClose={() => setEditTarget(null)}
        />
      )}

      <AddAutomationModal
        isOpen={isAddAutomationOpen}
        onClose={() => setIsAddAutomationOpen(false)}
      />

      <ImportAutomationModal
        isOpen={isImportOpen}
        spec={importSpec}
        isImporting={importMutation.isPending}
        onClose={() => {
          setIsImportOpen(false);
          setImportSpec(null);
        }}
        onImport={handleImportConfirm}
        onFile={handleImportFile}
      />
    </>,
  );
}
