import { fireEvent, render, screen } from "@testing-library/react"
import type { ComponentProps } from "react"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { PluginsPage } from "./PluginsPage"

type PluginsPageProps = ComponentProps<typeof PluginsPage>
type CatalogPlugin = PluginsPageProps["pluginCatalog"][number]
type InstalledPlugin = PluginsPageProps["installedPlugins"][number]
type McpDiagnostic = PluginsPageProps["pluginDiagnostics"][string]

function createPlugin(overrides: Partial<CatalogPlugin> = {}): CatalogPlugin {
  const id = overrides.id ?? "filesystem"

  return {
    id,
    name: overrides.name ?? "Filesystem",
    description: overrides.description ?? "Expose a local directory to MCP.",
    longDescription: overrides.longDescription,
    version: overrides.version ?? "1.0.0",
    publisher: overrides.publisher ?? "Anybox",
    category: overrides.category ?? "Code",
    iconUrl: overrides.iconUrl,
    thumbnailUrl: overrides.thumbnailUrl,
    heroImageUrl: overrides.heroImageUrl,
    screenshots: overrides.screenshots ?? [],
    tags: overrides.tags ?? [],
    brandColor: overrides.brandColor,
    risk: overrides.risk ?? "high",
    permissions: overrides.permissions ?? ["Read access inside the configured root path"],
    tools: overrides.tools ?? [
      {
        name: "read_file",
        title: "Read File",
        description: "Read files below the configured root.",
        readOnly: true,
      },
    ],
    mcpServers: overrides.mcpServers ?? [
      {
        id: "default",
        name: "Filesystem",
        description: "Expose local files.",
        risk: "high",
        permissions: ["Read access inside the configured root path"],
        tools: [
          {
            name: "read_file",
            description: "Read files below the configured root.",
            readOnly: true,
          },
        ],
        runtime: {
          transport: "stdio",
          command: "npx",
          args: ["-y", "@modelcontextprotocol/server-filesystem", "${ROOT_PATH}"],
          timeoutMs: 30000,
        },
      },
    ],
    skills: overrides.skills ?? [],
    connectorRequirements: overrides.connectorRequirements ?? [],
    connectors: overrides.connectors ?? overrides.apps ?? [],
    apps: overrides.apps ?? [],
    configFields: overrides.configFields ?? [
      {
        key: "ROOT_PATH",
        label: "Root path",
        type: "path",
        required: true,
      },
    ],
    installReview: overrides.installReview ?? ["Prefer a narrow project folder."],
    ...overrides,
  }
}

function createDocsPlugin(): CatalogPlugin {
  return createPlugin({
    id: "docs",
    name: "Docs",
    description: "Search connected documentation.",
    category: "Docs",
    risk: "medium",
    configFields: [],
    tools: [
      {
        name: "search_docs",
        title: "Search Docs",
        description: "Search docs.",
        readOnly: true,
      },
    ],
    mcpServers: [],
    skills: [
      {
        id: "plugin:docs:review",
        name: "Review Docs",
        description: "Review documentation output.",
        directory: "review",
      },
    ],
    apps: [
      {
        appID: "docs-api",
        name: "Docs API",
        description: "Remote docs connector.",
        credential: {
          key: "DOCS_API_KEY",
          label: "Docs API key",
          type: "password",
          required: true,
          secret: true,
        },
        runtime: {
          transport: "remote",
          serverUrl: "https://docs.example.test/mcp",
          allowedTools: {
            readOnly: true,
          },
          requireApproval: "always",
          timeoutMs: 30000,
        },
      },
    ],
    permissions: ["Sends requests to docs.example.test"],
    installReview: ["API keys are injected only at runtime."],
  })
}

function createOAuthPlugin(): CatalogPlugin {
  return createPlugin({
    id: "mail",
    name: "Mail",
    description: "Read connected mail.",
    category: "Docs",
    risk: "medium",
    configFields: [],
    tools: [],
    mcpServers: [],
    skills: [],
    apps: [
      {
        appID: "gmail",
        name: "Gmail",
        description: "Read Gmail over OAuth.",
        credential: {
          kind: "oauth",
          label: "Google account",
          clientID: "client",
          authorizationURL: "https://accounts.example.test/authorize",
          tokenURL: "https://accounts.example.test/token",
          scopes: ["gmail.readonly"],
        },
        runtime: {
          transport: "remote",
          serverUrl: "https://gmail.example.test/mcp",
          allowedTools: {
            readOnly: true,
          },
          requireApproval: "never",
        },
      },
    ],
    permissions: ["Reads mail metadata"],
    installReview: [],
  })
}

function createInstalledPlugin(overrides: Partial<InstalledPlugin> = {}): InstalledPlugin {
  return {
    pluginID: overrides.pluginID ?? "filesystem",
    version: overrides.version ?? "1.0.0",
    enabled: overrides.enabled ?? true,
    mcpServerID: overrides.mcpServerID ?? "plugin.filesystem",
    mcpServerIDs: overrides.mcpServerIDs ?? ["plugin.filesystem"],
    skillIDs: overrides.skillIDs ?? [],
    connectorIDs: overrides.connectorIDs ?? [],
    connectorRequirementIDs: overrides.connectorRequirementIDs ?? [],
    config: overrides.config ?? {
      ROOT_PATH: "C:\\Projects",
    },
    installedAt: overrides.installedAt ?? 1,
    updatedAt: overrides.updatedAt ?? 2,
    lastDiagnostic: overrides.lastDiagnostic,
    lastConnectorDiagnostics: overrides.lastConnectorDiagnostics,
  }
}

function createDiagnostic(overrides: Partial<McpDiagnostic> = {}): McpDiagnostic {
  return {
    serverID: overrides.serverID ?? "plugin.filesystem",
    enabled: overrides.enabled ?? true,
    ok: overrides.ok ?? true,
    toolCount: overrides.toolCount ?? 1,
    toolNames: overrides.toolNames ?? ["read_file"],
    tools: overrides.tools ?? [],
    error: overrides.error,
  }
}

function createProps(overrides: Partial<PluginsPageProps> = {}): PluginsPageProps {
  return {
    activePluginID: null,
    deletingPluginID: null,
    diagnosingPluginConnectorID: null,
    diagnosingPluginID: null,
    installingPluginID: null,
    installedPlugins: [],
    isLoading: false,
    loadError: null,
    message: null,
    connectorStatuses: [],
    pluginCatalog: [createPlugin()],
    pluginConnectorStatuses: {},
    pluginDiagnostics: {},
    pluginDraft: {
      pluginID: null,
      config: {},
      appApiKeys: {},
    },
    savingPluginConnectorID: null,
    updatingPluginID: null,
    onCancelInstalledPluginConnectorAuthFlow: vi.fn(),
    onDeleteInstalledPlugin: vi.fn(),
    onDeleteInstalledPluginConnectorApiKey: vi.fn(),
    onDeleteInstalledPluginConnectorAuthSession: vi.fn(),
    onDiagnoseInstalledPlugin: vi.fn(),
    onDiagnoseInstalledPluginConnector: vi.fn(),
    onDismissMessage: vi.fn(),
    onInstallPlugin: vi.fn(),
    onPluginDraftAppApiKeyChange: vi.fn(),
    onPluginDraftConfigChange: vi.fn(),
    onPluginDeselect: vi.fn(),
    onPluginSelect: vi.fn(),
    onSaveInstalledPluginConnectorApiKey: vi.fn(),
    onSaveInstalledPluginConfig: vi.fn(),
    onSetInstalledPluginEnabled: vi.fn(),
    onStartInstalledPluginConnectorAuthFlow: vi.fn(),
    ...overrides,
  }
}

describe("PluginsPage", () => {
  beforeEach(() => {
    window.desktop = undefined
  })

  it("renders the plugin marketplace without the development blocker", () => {
    const onInstallPlugin = vi.fn()
    render(<PluginsPage {...createProps({ onInstallPlugin })} />)

    expect(screen.getByRole("region", { name: "Plugin marketplace layout" })).toBeInTheDocument()
    expect(screen.queryByLabelText("Featured plugin spotlight")).not.toBeInTheDocument()
    expect(screen.queryByText("Plugin module is under development")).not.toBeInTheDocument()

    fireEvent.click(screen.getByRole("button", { name: "Install Filesystem" }))
    expect(onInstallPlugin).toHaveBeenCalledWith("filesystem")
  })

  it("opens selected plugin details as a second-level view and returns to the marketplace", () => {
    const onInstallPlugin = vi.fn()
    const onPluginDeselect = vi.fn()
    const onPluginSelect = vi.fn()
    const { rerender } = render(
      <PluginsPage
        {...createProps({
          onInstallPlugin,
          onPluginDeselect,
          onPluginSelect,
        })}
      />,
    )

    fireEvent.click(screen.getByRole("button", { name: "Filesystem not installed" }))
    expect(onPluginSelect).toHaveBeenCalledWith("filesystem")

    rerender(
      <PluginsPage
        {...createProps({
          activePluginID: "filesystem",
          onInstallPlugin,
          onPluginDeselect,
          onPluginSelect,
        })}
      />,
    )

    expect(screen.queryByRole("region", { name: "Plugin marketplace layout" })).not.toBeInTheDocument()
    expect(screen.getByRole("region", { name: "Selected plugin details" })).toBeInTheDocument()
    const breadcrumb = screen.getByRole("navigation", { name: "Plugin detail breadcrumb" })
    expect(breadcrumb).toHaveTextContent("插件")
    expect(screen.getByLabelText("Plugins top menu")).not.toContainElement(breadcrumb)
    expect(breadcrumb.closest(".plugins-page-main")).not.toBeNull()
    expect(screen.getByRole("heading", { name: "Filesystem", level: 1 })).toBeInTheDocument()
    expect(screen.queryByLabelText("Filesystem example prompts")).not.toBeInTheDocument()
    const installButton = screen.getByRole("button", { name: "Install Filesystem" })
    expect(installButton.closest(".plugins-detail-actions")).not.toBeNull()
    fireEvent.click(installButton)
    expect(onInstallPlugin).toHaveBeenCalledWith("filesystem")

    fireEvent.click(screen.getByRole("button", { name: "插件" }))
    expect(onPluginDeselect).toHaveBeenCalledTimes(1)
  })

  it("filters plugins by search and category and selects a plugin from the list", () => {
    const onPluginSelect = vi.fn()
    render(
      <PluginsPage
        {...createProps({
          pluginCatalog: [
            createPlugin(),
            createDocsPlugin(),
          ],
          onPluginSelect,
        })}
      />,
    )

    fireEvent.change(screen.getByLabelText("Search"), {
      target: {
        value: "docs",
      },
    })

    expect(screen.getByRole("button", { name: "Docs not installed" })).toBeInTheDocument()
    expect(screen.queryByRole("button", { name: "Filesystem not installed" })).not.toBeInTheDocument()

    fireEvent.change(screen.getByLabelText("Category"), {
      target: {
        value: "Docs",
      },
    })
    expect(screen.getByRole("button", { name: "Docs not installed" })).toBeInTheDocument()

    fireEvent.click(screen.getByRole("button", { name: "Docs not installed" }))
    expect(onPluginSelect).toHaveBeenCalledWith("docs")
  })

  it("can be embedded with an external search field", () => {
    render(
      <PluginsPage
        {...createProps({
          hideTopMenu: true,
          pluginCatalog: [createPlugin(), createDocsPlugin()],
          searchQuery: "docs",
        })}
      />,
    )

    expect(screen.queryByLabelText("Plugins top menu")).not.toBeInTheDocument()
    expect(screen.queryByRole("searchbox", { name: "Search" })).not.toBeInTheDocument()
    expect(screen.getByRole("button", { name: "Docs not installed" })).toBeInTheDocument()
    expect(screen.queryByRole("button", { name: "Filesystem not installed" })).not.toBeInTheDocument()
  })

  it("renders rich marketplace metadata in plugin details", () => {
    const imageUrl = "https://cdn.example.test/filesystem.png"
    render(
      <PluginsPage
        {...createProps({
          activePluginID: "filesystem",
          pluginCatalog: [
            createPlugin({
              longDescription: "A longer plugin marketplace description.",
              tags: ["files", "local"],
              thumbnailUrl: imageUrl,
              heroImageUrl: imageUrl,
              screenshots: [imageUrl],
              brandColor: "#112233",
            }),
          ],
        })}
      />,
    )

    expect(screen.getByText("A longer plugin marketplace description.")).toBeInTheDocument()
    expect(screen.getByText("files")).toBeInTheDocument()
    expect(screen.getByText("local")).toBeInTheDocument()
    expect(screen.getByAltText("Filesystem screenshot 1")).toHaveAttribute("src", imageUrl)
    expect(screen.getByText("#112233")).toBeInTheDocument()
  })

  it("renders plugin configuration fields before installation", () => {
    const onInstallPlugin = vi.fn()
    const onPluginDraftConfigChange = vi.fn()
    const plugin = {
      ...createOAuthPlugin(),
      id: "gmail",
      name: "Gmail",
      configFields: [
        {
          key: "GOOGLE_OAUTH_CLIENT_ID",
          label: "Google OAuth client ID",
          type: "text" as const,
          required: true,
          placeholder: "123.apps.googleusercontent.com",
          description: "OAuth client used for the Gmail connector.",
        },
      ],
    }

    render(
      <PluginsPage
        {...createProps({
          activePluginID: "gmail",
          pluginCatalog: [plugin],
          pluginDraft: {
            pluginID: "gmail",
            config: {
              GOOGLE_OAUTH_CLIENT_ID: "",
            },
            appApiKeys: {},
          },
          onInstallPlugin,
          onPluginDraftConfigChange,
        })}
      />,
    )

    const clientIDInput = screen.getByLabelText(/Google OAuth client ID/)
    expect(clientIDInput).toHaveAttribute("placeholder", "123.apps.googleusercontent.com")
    expect(screen.getByText("Required values are used when installing this plugin.")).toBeInTheDocument()

    fireEvent.change(clientIDInput, {
      target: {
        value: "client.apps.googleusercontent.com",
      },
    })
    expect(onPluginDraftConfigChange).toHaveBeenCalledWith(
      "GOOGLE_OAUTH_CLIENT_ID",
      "client.apps.googleusercontent.com",
    )

    fireEvent.click(screen.getByRole("button", { name: "Install Gmail" }))
    expect(onInstallPlugin).toHaveBeenCalledWith("gmail")
  })

  it("renders plugin info URLs as clickable desktop links", () => {
    const homepage = "https://example.test/filesystem"
    const documentationUrl = "https://docs.example.test/filesystem"
    const openExternalUrl = vi.fn().mockResolvedValue({
      ok: true,
      url: homepage,
    })
    window.desktop = {
      openExternalUrl,
    } as unknown as Window["desktop"]

    render(
      <PluginsPage
        {...createProps({
          activePluginID: "filesystem",
          pluginCatalog: [
            createPlugin({
              homepage,
              documentationUrl,
            }),
          ],
        })}
      />,
    )

    const homepageLink = screen.getByRole("link", { name: homepage })
    expect(homepageLink).toHaveAttribute("href", homepage)
    fireEvent.click(homepageLink)
    expect(openExternalUrl).toHaveBeenCalledWith({
      url: homepage,
    })

    const documentationLink = screen.getByRole("link", { name: documentationUrl })
    expect(documentationLink).toHaveAttribute("href", documentationUrl)
    fireEvent.click(documentationLink)
    expect(openExternalUrl).toHaveBeenCalledWith({
      url: documentationUrl,
    })
  })

  it("hides legacy management panels from selected plugin details", () => {
    const plugin = createDocsPlugin()
    const installed = createInstalledPlugin({
      pluginID: "docs",
      mcpServerID: "plugin.docs.app.docs-api",
      mcpServerIDs: ["plugin.docs.app.docs-api"],
      skillIDs: ["plugin:docs:review"],
      connectorIDs: ["plugin-app:docs:docs-api"],
      config: {},
    })
    render(
      <PluginsPage
        {...createProps({
          activePluginID: "docs",
          pluginCatalog: [plugin],
          installedPlugins: [installed],
          pluginConnectorStatuses: {
            docs: [
              {
                pluginID: "docs",
                appID: "docs-api",
                connectorID: "plugin-app:docs:docs-api",
                connected: true,
                credentialKind: "api_key",
                authStatus: "connected",
                credentialLabel: "Docs API key",
                generatedMcpServerID: "plugin.docs.app.docs-api",
                lastDiagnostic: createDiagnostic({
                  serverID: "plugin.docs.app.docs-api",
                  toolNames: ["search_docs"],
                }),
              },
            ],
          },
          pluginDiagnostics: {
            docs: createDiagnostic({
              serverID: "plugin.docs.app.docs-api",
              toolNames: ["search_docs"],
            }),
          },
        })}
      />,
    )

    expect(screen.getByRole("region", { name: "Selected plugin details" })).toBeInTheDocument()
    const installedStatus = screen.getByLabelText("Docs installed")
    expect(installedStatus).toHaveTextContent("Installed")
    expect(installedStatus.closest(".plugins-detail-actions")).not.toBeNull()
    expect(screen.getByRole("button", { name: "Uninstall Docs" })).toBeInTheDocument()
    expect(screen.getByText("Docs API")).toBeInTheDocument()
    expect(screen.getByText("Review Docs")).toBeInTheDocument()

    expect(screen.queryByText("Manage Plugin")).not.toBeInTheDocument()
    expect(screen.queryByRole("heading", { name: "Tools Preview" })).not.toBeInTheDocument()
    expect(screen.queryByRole("heading", { name: "Included Capabilities" })).not.toBeInTheDocument()
    expect(screen.queryByRole("heading", { name: "MCP Bindings" })).not.toBeInTheDocument()
    expect(screen.queryByRole("heading", { name: "Install Review" })).not.toBeInTheDocument()
    expect(screen.queryByRole("heading", { name: "Connectors" })).not.toBeInTheDocument()
    expect(screen.queryByRole("heading", { name: "Plugin Values" })).not.toBeInTheDocument()
    expect(screen.queryByLabelText(/Docs API key/)).not.toBeInTheDocument()
    expect(screen.queryByRole("button", { name: "Update key" })).not.toBeInTheDocument()
    expect(screen.queryByRole("button", { name: "Diagnose" })).not.toBeInTheDocument()
    expect(screen.queryByText("Connector reachable. Tools: search_docs")).not.toBeInTheDocument()
  })

  it("uninstalls an installed plugin from the selected plugin details", () => {
    const onDeleteInstalledPlugin = vi.fn()
    render(
      <PluginsPage
        {...createProps({
          activePluginID: "filesystem",
          installedPlugins: [createInstalledPlugin()],
          onDeleteInstalledPlugin,
        })}
      />,
    )

    const uninstallButton = screen.getByRole("button", { name: "Uninstall Filesystem" })
    expect(uninstallButton.closest(".plugins-detail-actions")).not.toBeNull()

    fireEvent.click(uninstallButton)
    expect(onDeleteInstalledPlugin).toHaveBeenCalledWith("filesystem")
  })

  it("shows progress while uninstalling an installed plugin", () => {
    render(
      <PluginsPage
        {...createProps({
          activePluginID: "filesystem",
          deletingPluginID: "filesystem",
          installedPlugins: [createInstalledPlugin()],
        })}
      />,
    )

    const uninstallButton = screen.getByRole("button", { name: "Uninstall Filesystem" })
    expect(uninstallButton).toBeDisabled()
    expect(uninstallButton).toHaveTextContent("Uninstalling...")
  })

  it("expands included content rows and switches the visible detail", () => {
    const plugin = createDocsPlugin()

    render(
      <PluginsPage
        {...createProps({
          activePluginID: "docs",
          pluginCatalog: [plugin],
          installedPlugins: [
            createInstalledPlugin({
              pluginID: "docs",
              mcpServerID: "plugin.docs.app.docs-api",
              mcpServerIDs: ["plugin.docs.app.docs-api"],
              skillIDs: ["plugin:docs:review"],
              connectorIDs: ["plugin-app:docs:docs-api"],
              config: {},
            }),
          ],
          pluginConnectorStatuses: {
            docs: [
              {
                pluginID: "docs",
                appID: "docs-api",
                connectorID: "plugin-app:docs:docs-api",
                connected: true,
                credentialKind: "api_key",
                authStatus: "connected",
                credentialLabel: "Docs API key",
                generatedMcpServerID: "plugin.docs.app.docs-api",
              },
            ],
          },
        })}
      />,
    )

    fireEvent.click(screen.getByRole("button", { name: "Show details for Review Docs" }))
    expect(screen.getByText("Skill ID")).toBeInTheDocument()
    expect(screen.getByText("plugin:docs:review")).toBeInTheDocument()
    expect(screen.getByText("Directory")).toBeInTheDocument()
    expect(screen.getByText("review")).toBeInTheDocument()

    fireEvent.click(screen.getByRole("button", { name: "Show details for Docs API" }))
    expect(screen.queryByText("plugin:docs:review")).not.toBeInTheDocument()
    expect(screen.getByText("Connector ID")).toBeInTheDocument()
    expect(screen.getByText("plugin-app:docs:docs-api")).toBeInTheDocument()
    expect(screen.getByText("Credential")).toBeInTheDocument()
    expect(screen.getAllByText("Docs API key").length).toBeGreaterThan(0)
    expect(screen.getByText("https://docs.example.test/mcp")).toBeInTheDocument()
  })

  it("shows platform connector requirement connection state from global connectors", () => {
    const plugin = createPlugin({
      id: "mail-helper",
      name: "Mail Helper",
      mcpServers: [],
      skills: [],
      apps: [],
      configFields: [],
      connectorRequirements: [
        {
          connector: "gmail",
          reason: "Search and summarize mailbox context.",
          tools: ["search_email_ids"],
          permissions: ["Read Gmail metadata"],
        },
      ],
    })

    render(
      <PluginsPage
        {...createProps({
          activePluginID: "mail-helper",
          pluginCatalog: [plugin],
          installedPlugins: [
            createInstalledPlugin({
              pluginID: "mail-helper",
              mcpServerID: "plugin.mail-helper",
              mcpServerIDs: [],
              connectorRequirementIDs: ["connector:gmail:default"],
              config: {},
            }),
          ],
          connectorStatuses: [
            {
              connectorID: "connector:gmail:default",
              definitionID: "gmail",
              name: "Gmail",
              connected: true,
              available: true,
              authStatus: "connected",
              credentialKind: "oauth",
              credentialLabel: "Google account",
              email: "person@example.test",
              generatedMcpServerID: "connector.gmail.default",
            },
          ],
        })}
      />,
    )

    fireEvent.click(screen.getByRole("button", { name: "Show details for gmail" }))
    expect(screen.getByText("Platform connector")).toBeInTheDocument()
    expect(screen.getByText("Connected")).toBeInTheDocument()
    expect(screen.getByText("connector:gmail:default")).toBeInTheDocument()
    expect(screen.getByText("person@example.test")).toBeInTheDocument()
    expect(screen.getByText("connector.gmail.default")).toBeInTheDocument()
  })

  it("shows OAuth connector sign-in controls in included app details", () => {
    const plugin = createOAuthPlugin()
    const onStartInstalledPluginConnectorAuthFlow = vi.fn()
    const onDeleteInstalledPluginConnectorAuthSession = vi.fn()

    render(
      <PluginsPage
        {...createProps({
          activePluginID: "mail",
          pluginCatalog: [plugin],
          installedPlugins: [
            createInstalledPlugin({
              pluginID: "mail",
              mcpServerID: "plugin.mail.app.gmail",
              mcpServerIDs: ["plugin.mail.app.gmail"],
              connectorIDs: ["plugin-app:mail:gmail"],
              config: {},
            }),
          ],
          pluginConnectorStatuses: {
            mail: [
              {
                pluginID: "mail",
                appID: "gmail",
                connectorID: "plugin-app:mail:gmail",
                connected: false,
                credentialKind: "oauth",
                authStatus: "not_connected",
                credentialLabel: "Google account",
                generatedMcpServerID: "plugin.mail.app.gmail",
              },
            ],
          },
          onStartInstalledPluginConnectorAuthFlow,
          onDeleteInstalledPluginConnectorAuthSession,
        })}
      />,
    )

    expect(screen.getByText("Credential kind")).toBeInTheDocument()
    expect(screen.getByText("OAuth")).toBeInTheDocument()
    fireEvent.click(screen.getByRole("button", { name: "Sign in" }))
    expect(onStartInstalledPluginConnectorAuthFlow).toHaveBeenCalledWith("mail", "gmail")
    expect(screen.queryByRole("textbox", { name: /Google account/ })).not.toBeInTheDocument()
  })
})
