"use client";

export const dynamic = "force-dynamic";

import Link from "next/link";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";

import { useAuth } from "@/auth/clerk";
import { useQueryClient } from "@tanstack/react-query";

import { ApiError } from "@/api/mutator";
import {
  type listGatewaysApiV1GatewaysGetResponse,
  useListGatewaysApiV1GatewaysGet,
} from "@/api/generated/gateways/gateways";
import type { MarketplaceSkillCardRead } from "@/api/generated/model";
import {
  listMarketplaceSkillsApiV1SkillsMarketplaceGet,
  type listMarketplaceSkillsApiV1SkillsMarketplaceGetResponse,
  useInstallMarketplaceSkillApiV1SkillsMarketplaceSkillIdInstallPost,
  useListMarketplaceSkillsApiV1SkillsMarketplaceGet,
  useUninstallMarketplaceSkillApiV1SkillsMarketplaceSkillIdUninstallPost,
} from "@/api/generated/skills-marketplace/skills-marketplace";
import {
  type listSkillPacksApiV1SkillsPacksGetResponse,
  useListSkillPacksApiV1SkillsPacksGet,
} from "@/api/generated/skills/skills";
import { SkillInstallDialog } from "@/components/skills/SkillInstallDialog";
import { MarketplaceSkillsTable } from "@/components/skills/MarketplaceSkillsTable";
import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout";
import { buttonVariants } from "@/components/ui/button";
import { useOrganizationMembership } from "@/lib/use-organization-membership";
import {
  normalizeRepoSourceUrl,
  repoBaseFromSkillSourceUrl,
} from "@/lib/skills-source";
import { useUrlSorting } from "@/lib/use-url-sorting";

const MARKETPLACE_SKILLS_SORTABLE_COLUMNS = [
  "name",
  "category",
  "risk",
  "source",
  "updated_at",
];

export default function SkillsMarketplacePage() {
  const queryClient = useQueryClient();
  const searchParams = useSearchParams();
  const { isSignedIn } = useAuth();
  const { isAdmin } = useOrganizationMembership(isSignedIn);
  const [selectedSkill, setSelectedSkill] = useState<MarketplaceSkillCardRead | null>(null);
  const [gatewayInstalledById, setGatewayInstalledById] = useState<
    Record<string, boolean>
  >({});
  const [installedGatewayNamesBySkillId, setInstalledGatewayNamesBySkillId] = useState<
    Record<string, string[]>
  >({});
  const [isGatewayStatusLoading, setIsGatewayStatusLoading] = useState(false);
  const [gatewayStatusError, setGatewayStatusError] = useState<string | null>(null);
  const [installingGatewayId, setInstallingGatewayId] = useState<string | null>(null);

  const { sorting, onSortingChange } = useUrlSorting({
    allowedColumnIds: MARKETPLACE_SKILLS_SORTABLE_COLUMNS,
    defaultSorting: [{ id: "name", desc: false }],
    paramPrefix: "skills_marketplace",
  });

  const gatewaysQuery = useListGatewaysApiV1GatewaysGet<
    listGatewaysApiV1GatewaysGetResponse,
    ApiError
  >(undefined, {
    query: {
      enabled: Boolean(isSignedIn && isAdmin),
      refetchOnMount: "always",
      refetchInterval: 30_000,
    },
  });

  const gateways = useMemo(
    () =>
      gatewaysQuery.data?.status === 200
        ? (gatewaysQuery.data.data.items ?? [])
        : [],
    [gatewaysQuery.data],
  );

  const resolvedGatewayId = gateways[0]?.id ?? "";

  const skillsQuery = useListMarketplaceSkillsApiV1SkillsMarketplaceGet<
    listMarketplaceSkillsApiV1SkillsMarketplaceGetResponse,
    ApiError
  >(
    { gateway_id: resolvedGatewayId },
    {
      query: {
        enabled: Boolean(isSignedIn && isAdmin && resolvedGatewayId),
        refetchOnMount: "always",
        refetchInterval: 15_000,
      },
    },
  );

  const skills = useMemo<MarketplaceSkillCardRead[]>(
    () => (skillsQuery.data?.status === 200 ? skillsQuery.data.data : []),
    [skillsQuery.data],
  );

  const packsQuery = useListSkillPacksApiV1SkillsPacksGet<
    listSkillPacksApiV1SkillsPacksGetResponse,
    ApiError
  >({
    query: {
      enabled: Boolean(isSignedIn && isAdmin),
      refetchOnMount: "always",
    },
  });

  const packs = useMemo(
    () => (packsQuery.data?.status === 200 ? packsQuery.data.data : []),
    [packsQuery.data],
  );

  const selectedPackId = searchParams.get("packId");
  const selectedPack = useMemo(
    () => packs.find((pack) => pack.id === selectedPackId) ?? null,
    [packs, selectedPackId],
  );

  const visibleSkills = useMemo(() => {
    if (!selectedPack) return skills;
    const selectedRepo = normalizeRepoSourceUrl(selectedPack.source_url);
    return skills.filter((skill) => {
      const skillRepo = repoBaseFromSkillSourceUrl(skill.source_url);
      return skillRepo === selectedRepo;
    });
  }, [selectedPack, skills]);

  const loadSkillsByGateway = useCallback(async () => {
    // NOTE: This is technically N+1 (one request per gateway). We intentionally
    // parallelize requests to keep the UI responsive and avoid slow sequential
    // fetches. If this becomes a bottleneck for large gateway counts, add a
    // backend batch endpoint to return installation state across all gateways.
    const gatewaySkills = await Promise.all(
      gateways.map(async (gateway) => {
        const response = await listMarketplaceSkillsApiV1SkillsMarketplaceGet({
          gateway_id: gateway.id,
        });
        return {
          gatewayId: gateway.id,
          gatewayName: gateway.name,
          skills: response.status === 200 ? response.data : [],
        };
      }),
    );

    return gatewaySkills;
  }, [gateways]);

  const updateInstalledGatewayNames = useCallback(
    ({
      skillId,
      gatewayName,
      installed,
    }: {
      skillId: string;
      gatewayName: string;
      installed: boolean;
    }) => {
      setInstalledGatewayNamesBySkillId((previous) => {
        const installedOn = previous[skillId] ?? [];
        if (installed) {
          if (installedOn.includes(gatewayName)) return previous;
          return {
            ...previous,
            [skillId]: [...installedOn, gatewayName],
          };
        }
        return {
          ...previous,
          [skillId]: installedOn.filter((name) => name !== gatewayName),
        };
      });
    },
    [],
  );

  useEffect(() => {
    let cancelled = false;

    const loadInstalledGatewaysBySkill = async () => {
      if (!isSignedIn || !isAdmin || gateways.length === 0 || skills.length === 0) {
        setInstalledGatewayNamesBySkillId({});
        return;
      }

      try {
        const gatewaySkills = await Promise.all(
          gateways.map(async (gateway) => {
            const response = await listMarketplaceSkillsApiV1SkillsMarketplaceGet({
              gateway_id: gateway.id,
            });
            return {
              gatewayId: gateway.id,
              gatewayName: gateway.name,
              skills: response.status === 200 ? response.data : [],
            };
          }),
        );

        if (cancelled) return;

        const nextInstalledGatewayNamesBySkillId: Record<string, string[]> = {};
        for (const skill of skills) {
          nextInstalledGatewayNamesBySkillId[skill.id] = [];
        }

        for (const { gatewayName, skills: gatewaySkillRows } of gatewaySkills) {
          for (const skill of gatewaySkillRows) {
            if (!skill.installed) continue;
            if (!nextInstalledGatewayNamesBySkillId[skill.id]) continue;
            nextInstalledGatewayNamesBySkillId[skill.id].push(gatewayName);
          }
        }

        setInstalledGatewayNamesBySkillId(nextInstalledGatewayNamesBySkillId);
      } catch {
        if (cancelled) return;
        setInstalledGatewayNamesBySkillId({});
      }
    };

    void loadInstalledGatewaysBySkill();

    return () => {
      cancelled = true;
    };
  }, [gateways, isAdmin, isSignedIn, skills]);

  const installMutation =
    useInstallMarketplaceSkillApiV1SkillsMarketplaceSkillIdInstallPost<ApiError>(
      {
        mutation: {
          onSuccess: async (_, variables) => {
            await queryClient.invalidateQueries({
              queryKey: ["/api/v1/skills/marketplace"],
            });
            setGatewayInstalledById((previous) => ({
              ...previous,
              [variables.params.gateway_id]: true,
            }));
            const gatewayName =
              gateways.find((gateway) => gateway.id === variables.params.gateway_id)?.name;
            if (gatewayName) {
              updateInstalledGatewayNames({
                skillId: variables.skillId,
                gatewayName,
                installed: true,
              });
            }
          },
        },
      },
      queryClient,
    );

  const uninstallMutation =
    useUninstallMarketplaceSkillApiV1SkillsMarketplaceSkillIdUninstallPost<ApiError>(
      {
        mutation: {
          onSuccess: async (_, variables) => {
            await queryClient.invalidateQueries({
              queryKey: ["/api/v1/skills/marketplace"],
            });
            setGatewayInstalledById((previous) => ({
              ...previous,
              [variables.params.gateway_id]: false,
            }));
            const gatewayName =
              gateways.find((gateway) => gateway.id === variables.params.gateway_id)?.name;
            if (gatewayName) {
              updateInstalledGatewayNames({
                skillId: variables.skillId,
                gatewayName,
                installed: false,
              });
            }
          },
        },
      },
      queryClient,
    );

  useEffect(() => {
    let cancelled = false;

    const loadGatewayStatus = async () => {
      if (!selectedSkill) {
        setGatewayInstalledById({});
        setGatewayStatusError(null);
        setIsGatewayStatusLoading(false);
        return;
      }

      if (gateways.length === 0) {
        setGatewayInstalledById({});
        setGatewayStatusError(null);
        setIsGatewayStatusLoading(false);
        return;
      }

      setIsGatewayStatusLoading(true);
      setGatewayStatusError(null);
      try {
        const gatewaySkills = await loadSkillsByGateway();
        const entries = gatewaySkills.map(({ gatewayId, skills: gatewaySkillRows }) => {
          const row = gatewaySkillRows.find((skill) => skill.id === selectedSkill.id);
          return [gatewayId, Boolean(row?.installed)] as const;
        });
        if (cancelled) return;
        setGatewayInstalledById(Object.fromEntries(entries));
      } catch (error) {
        if (cancelled) return;
        setGatewayStatusError(
          error instanceof Error ? error.message : "Unable to load gateway status.",
        );
      } finally {
        if (!cancelled) {
          setIsGatewayStatusLoading(false);
        }
      }
    };

    void loadGatewayStatus();

    return () => {
      cancelled = true;
    };
  }, [gateways, loadSkillsByGateway, selectedSkill]);

  const mutationError =
    installMutation.error?.message ?? uninstallMutation.error?.message ?? null;

  const isMutating = installMutation.isPending || uninstallMutation.isPending;

  const handleGatewayInstallAction = async (
    gatewayId: string,
    isInstalled: boolean,
  ) => {
    if (!selectedSkill) return;
    setInstallingGatewayId(gatewayId);
    try {
      if (isInstalled) {
        await uninstallMutation.mutateAsync({
          skillId: selectedSkill.id,
          params: { gateway_id: gatewayId },
        });
      } else {
        await installMutation.mutateAsync({
          skillId: selectedSkill.id,
          params: { gateway_id: gatewayId },
        });
      }
    } finally {
      setInstallingGatewayId(null);
    }
  };

  return (
    <>
      <DashboardPageLayout
        signedOut={{
          message: "Sign in to manage marketplace skills.",
          forceRedirectUrl: "/skills/marketplace",
        }}
        title="Skills Marketplace"
        description={
          selectedPack
            ? `${visibleSkills.length} skill${
                visibleSkills.length === 1 ? "" : "s"
              } for ${selectedPack.name}.`
            : `${visibleSkills.length} skill${
                visibleSkills.length === 1 ? "" : "s"
              } synced from packs.`
        }
        isAdmin={isAdmin}
        adminOnlyMessage="Only organization owners and admins can manage skills."
        stickyHeader
      >
        <div className="space-y-6">
          {gateways.length === 0 ? (
            <div className="rounded-xl border border-slate-200 bg-white p-6 text-sm text-slate-600 shadow-sm">
              <p className="font-medium text-slate-900">No gateways available yet.</p>
              <p className="mt-2">
                Create a gateway first, then return here to manage installs.
              </p>
              <Link
                href="/gateways/new"
                className={`${buttonVariants({ variant: "primary", size: "md" })} mt-4`}
              >
                Create gateway
              </Link>
            </div>
          ) : (
            <>
              <div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
                <MarketplaceSkillsTable
                  skills={visibleSkills}
                  installedGatewayNamesBySkillId={installedGatewayNamesBySkillId}
                  isLoading={skillsQuery.isLoading}
                  sorting={sorting}
                  onSortingChange={onSortingChange}
                  stickyHeader
                  isMutating={isMutating}
                  onSkillClick={setSelectedSkill}
                  emptyState={{
                    title: "No marketplace skills yet",
                    description: "Add packs first, then synced skills will appear here.",
                    actionHref: "/skills/packs/new",
                    actionLabel: "Add your first pack",
                  }}
                />
              </div>
            </>
          )}

          {skillsQuery.error ? (
            <p className="text-sm text-rose-600">{skillsQuery.error.message}</p>
          ) : null}
          {packsQuery.error ? (
            <p className="text-sm text-rose-600">{packsQuery.error.message}</p>
          ) : null}
          {mutationError ? <p className="text-sm text-rose-600">{mutationError}</p> : null}
        </div>
      </DashboardPageLayout>

      <SkillInstallDialog
        selectedSkill={selectedSkill}
        gateways={gateways}
        gatewayInstalledById={gatewayInstalledById}
        isGatewayStatusLoading={isGatewayStatusLoading}
        installingGatewayId={installingGatewayId}
        isMutating={isMutating}
        gatewayStatusError={gatewayStatusError}
        mutationError={mutationError}
        onOpenChange={(open) => {
          if (!open) {
            setSelectedSkill(null);
          }
        }}
        onToggleInstall={(gatewayId, isInstalled) => {
          void handleGatewayInstallAction(gatewayId, isInstalled);
        }}
      />
    </>
  );
}
