import { useT } from "@agent-native/core/client/i18n";

import { filterOtherApps, type ConnectedAppSummary } from "../lib/other-apps";
import type { WorkspaceAppId } from "../lib/other-apps";
import { cn } from "../lib/utils";
import { isDefaultWorkspaceAppHiddenId } from "../lib/workspace-apps";
import { ActionQueryError } from "./action-query-error";
import {
  APP_LIST_GRID_CLASS,
  APP_LIST_GRID_ROW_CLASS,
  AppList,
} from "./app-list-row";
import { ConnectedAppCard } from "./connected-app-card";
import { Skeleton } from "./ui/skeleton";
import {
  WorkspaceTemplateCard,
  type CuratedWorkspaceTemplate,
  type CuratedWorkspaceTemplatesResult,
  type WorkspaceTemplateLabels,
} from "./workspace-template-card";

export type OtherAppEntry =
  | { kind: "template"; template: CuratedWorkspaceTemplate }
  | { kind: "connected"; app: ConnectedAppSummary };

function templateKey(template: CuratedWorkspaceTemplate): string {
  return (template.templateId || template.id || template.appId || template.name)
    .trim()
    .toLowerCase();
}

function getTemplateItems(
  result: CuratedWorkspaceTemplatesResult | undefined,
): CuratedWorkspaceTemplate[] {
  if (!result) return [];
  return Array.isArray(result) ? result : result.templates;
}

export function otherAppEntryMatchesQuery(
  entry: OtherAppEntry,
  query: string,
): boolean {
  const normalizedQuery = query.trim().toLowerCase();
  if (!normalizedQuery) return true;
  const searchable =
    entry.kind === "template"
      ? `${entry.template.name} ${entry.template.description ?? ""}`
      : `${entry.app.name} ${entry.app.description ?? ""}`;
  return searchable.toLowerCase().includes(normalizedQuery);
}

export function filterOtherAppEntries(
  entries: OtherAppEntry[],
  query: string,
): OtherAppEntry[] {
  return entries.filter((entry) => otherAppEntryMatchesQuery(entry, query));
}

export function mergeOtherAppEntries({
  templates,
  connectedApps,
  workspaceApps,
}: {
  templates?: CuratedWorkspaceTemplatesResult;
  connectedApps: ConnectedAppSummary[];
  workspaceApps: WorkspaceAppId[];
}): OtherAppEntry[] {
  const workspaceAppIds = new Set(
    workspaceApps.map((app) => app.id.trim().toLowerCase()),
  );
  const seen = new Set<string>();
  const entries: OtherAppEntry[] = [];

  for (const template of getTemplateItems(templates)) {
    const id = templateKey(template);
    if (
      !id ||
      isDefaultWorkspaceAppHiddenId(id) ||
      template.installed ||
      workspaceAppIds.has(id)
    )
      continue;
    seen.add(id);
    entries.push({ kind: "template", template });
  }

  for (const app of filterOtherApps(connectedApps, workspaceApps)) {
    const id = app.id.trim().toLowerCase();
    if (seen.has(id)) continue;
    seen.add(id);
    entries.push({ kind: "connected", app });
  }

  return entries;
}

export function OtherAppsSection({
  templates,
  connectedApps = [],
  workspaceApps,
  templatesLoading = false,
  connectedAppsLoading = false,
  templatesError,
  connectedAppsError,
  onRetryTemplates,
  onRetryConnectedApps,
  templateLabels,
  onRemixSuccess,
  query = "",
  heading = "Other apps",
  embeddedInList = false,
  className,
}: {
  templates?: CuratedWorkspaceTemplatesResult;
  connectedApps?: ConnectedAppSummary[];
  workspaceApps: WorkspaceAppId[];
  templatesLoading?: boolean;
  connectedAppsLoading?: boolean;
  templatesError?: Error | null;
  connectedAppsError?: Error | null;
  onRetryTemplates?: () => void;
  onRetryConnectedApps?: () => void;
  templateLabels?: Partial<WorkspaceTemplateLabels>;
  onRemixSuccess?: (
    result: unknown,
    template: CuratedWorkspaceTemplate,
  ) => void;
  query?: string;
  heading?: string | null;
  embeddedInList?: boolean;
  className?: string;
}) {
  const t = useT();
  const entries = filterOtherAppEntries(
    mergeOtherAppEntries({
      templates,
      connectedApps,
      workspaceApps,
    }),
    query,
  );
  const isLoading = templatesLoading || connectedAppsLoading;
  const hasError = Boolean(templatesError || connectedAppsError);

  if (!isLoading && !hasError && entries.length === 0) return null;

  const entryCards = entries.map((entry) =>
    entry.kind === "template" ? (
      <WorkspaceTemplateCard
        key={`template:${templateKey(entry.template)}`}
        template={entry.template}
        labels={templateLabels}
        catalog
        className={APP_LIST_GRID_ROW_CLASS}
        onRemixSuccess={onRemixSuccess}
      />
    ) : (
      <ConnectedAppCard
        key={`connected:${entry.app.id}`}
        app={entry.app}
        className={APP_LIST_GRID_ROW_CLASS}
      />
    ),
  );

  const content = (
    <>
      {heading ? (
        <div className="flex min-w-0 items-center">
          <h2 className="truncate text-sm font-semibold text-foreground">
            {t("dispatch.pages.otherApps", { defaultValue: heading })}
          </h2>
        </div>
      ) : null}

      {templatesError ? (
        <ActionQueryError
          error={templatesError}
          onRetry={() => onRetryTemplates?.()}
        />
      ) : null}
      {connectedAppsError ? (
        <ActionQueryError
          error={connectedAppsError}
          onRetry={() => onRetryConnectedApps?.()}
        />
      ) : null}

      {isLoading && entries.length === 0 ? (
        embeddedInList ? (
          <OtherAppsSkeletonRows />
        ) : (
          <OtherAppsSkeletonList />
        )
      ) : entries.length > 0 ? (
        embeddedInList ? (
          entryCards
        ) : (
          <AppList className={APP_LIST_GRID_CLASS}>{entryCards}</AppList>
        )
      ) : null}
    </>
  );

  if (embeddedInList) return content;

  return <section className={cn("space-y-3", className)}>{content}</section>;
}

function OtherAppsSkeletonList() {
  return (
    <AppList className={APP_LIST_GRID_CLASS}>
      {Array.from({ length: 4 }).map((_, index) => (
        <div
          key={index}
          className={cn(
            "flex min-w-0 items-center gap-3 border-b px-4 py-3.5 last:border-b-0",
            APP_LIST_GRID_ROW_CLASS,
          )}
        >
          <Skeleton className="size-8 shrink-0 rounded-xl" />
          <div className="min-w-0 flex-1 space-y-2">
            <Skeleton className="h-4 w-32" />
            <Skeleton className="h-3 w-2/3" />
          </div>
          <Skeleton className="h-9 w-24 shrink-0 rounded-md" />
        </div>
      ))}
    </AppList>
  );
}

function OtherAppsSkeletonRows() {
  return (
    <>
      {Array.from({ length: 4 }).map((_, index) => (
        <div
          key={index}
          className={cn(
            "flex min-w-0 items-center gap-3 border-b px-4 py-3.5 last:border-b-0",
            APP_LIST_GRID_ROW_CLASS,
          )}
        >
          <Skeleton className="size-8 shrink-0 rounded-xl" />
          <div className="min-w-0 flex-1 space-y-2">
            <Skeleton className="h-4 w-32" />
            <Skeleton className="h-3 w-2/3" />
          </div>
          <Skeleton className="h-9 w-24 shrink-0 rounded-md" />
        </div>
      ))}
    </>
  );
}
