import { render, screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { MemoryRouter } from "react-router-dom"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ChannelsPage } from "./ChannelsPage"
import { AuthProvider } from "@/auth/AuthContext"
import { api } from "@/api/client"
import { vi, beforeEach, test, expect } from "vitest"
import type { Channel } from "@/api/types"

const PROJECT_ID = "p1"
const channels: Channel[] = [
  { id: "c1", type: "telegram", config: { chat_id: "123456" }, enabled: true },
  { id: "c2", type: "webhook", config: { url: "https://hooks.example.com/x" }, enabled: false },
]

function renderPage() {
  const qc = new QueryClient()
  return render(
    <QueryClientProvider client={qc}>
      <AuthProvider>
        <MemoryRouter initialEntries={["/channels"]}>
          <ChannelsPage />
        </MemoryRouter>
      </AuthProvider>
    </QueryClientProvider>,
  )
}

beforeEach(() => {
  vi.restoreAllMocks()
  vi.spyOn(api.auth, "me").mockResolvedValue({
    user: { id: "u1", email: "a@b.com" },
    project: { id: PROJECT_ID, name: "Default" },
  })
  vi.spyOn(api, "listChannels").mockResolvedValue(channels)
})

test("отрисовывает список каналов без секрета", async () => {
  renderPage()

  expect(await screen.findByText("telegram")).toBeInTheDocument()
  expect(screen.getByText("webhook")).toBeInTheDocument()
  expect(screen.getByText(/123456/)).toBeInTheDocument()
  expect(screen.getByText(/hooks\.example\.com/)).toBeInTheDocument()

  expect(document.body.textContent).not.toMatch(/bot_token/i)
  expect(screen.queryByDisplayValue(/123456/)).not.toBeInTheDocument()
})

test("formatConfig — вайтлист по типу канала не печатает незнакомые поля config", async () => {
  vi.spyOn(api, "listChannels").mockResolvedValue([
    { id: "c5", type: "webhook", config: { url: "https://x", token: "LEAK" }, enabled: true },
  ])
  renderPage()

  expect(await screen.findByText(/url: https:\/\/x/)).toBeInTheDocument()
  expect(document.body.textContent).not.toMatch(/LEAK/)
})

test("создание telegram-канала собирает config.chat_id + secret=bot_token", async () => {
  const createSpy = vi.spyOn(api, "createChannel").mockResolvedValue({
    id: "c3", type: "telegram", config: { chat_id: "999" }, enabled: true,
  })
  const user = userEvent.setup()
  renderPage()

  await screen.findByText("telegram")

  await user.click(screen.getByRole("combobox", { name: /тип канала/i }))
  await user.click(await screen.findByRole("option", { name: /telegram/i }))

  await user.type(screen.getByLabelText(/chat id/i), "999")
  await user.type(screen.getByLabelText(/bot token/i), "SECRET_TOKEN")
  await user.click(screen.getByRole("button", { name: /добавить канал/i }))

  await waitFor(() =>
    expect(createSpy).toHaveBeenCalledWith(PROJECT_ID, {
      type: "telegram",
      config: { chat_id: "999" },
      secret: "SECRET_TOKEN",
    }),
  )

  expect(document.body.textContent).not.toMatch(/SECRET_TOKEN/)
})

test("создание webhook-канала собирает config.url без секрета", async () => {
  const createSpy = vi.spyOn(api, "createChannel").mockResolvedValue({
    id: "c4", type: "webhook", config: { url: "https://hooks.example.com/y" }, enabled: true,
  })
  const user = userEvent.setup()
  renderPage()

  await screen.findByText("telegram")

  await user.click(screen.getByRole("combobox", { name: /тип канала/i }))
  await user.click(await screen.findByRole("option", { name: /webhook/i }))

  await user.type(screen.getByLabelText(/url/i), "https://hooks.example.com/y")
  await user.click(screen.getByRole("button", { name: /добавить канал/i }))

  await waitFor(() =>
    expect(createSpy).toHaveBeenCalledWith(PROJECT_ID, {
      type: "webhook",
      config: { url: "https://hooks.example.com/y" },
      secret: "",
    }),
  )
})

test("удаление канала вызывает api.deleteChannel", async () => {
  const deleteSpy = vi.spyOn(api, "deleteChannel").mockResolvedValue(undefined)
  vi.spyOn(window, "confirm").mockReturnValue(true)
  const user = userEvent.setup()
  renderPage()

  await screen.findByText("telegram")

  await user.click(screen.getByRole("button", { name: /удалить канал telegram/i }))

  await waitFor(() => expect(deleteSpy).toHaveBeenCalledWith(PROJECT_ID, "c1"))
})

test("кнопка «Тест» вызывает api.testChannel", async () => {
  const testSpy = vi.spyOn(api, "testChannel").mockResolvedValue({ status: "ok" })
  const user = userEvent.setup()
  renderPage()

  await screen.findByText("telegram")

  const testButtons = screen.getAllByRole("button", { name: /тест/i })
  await user.click(testButtons[0])

  await waitFor(() => expect(testSpy).toHaveBeenCalledWith(PROJECT_ID, "c1"))
})

test("ошибка тест-отправки отображается как alert", async () => {
  vi.spyOn(api, "testChannel").mockRejectedValue(new Error("Канал не отвечает"))
  const user = userEvent.setup()
  renderPage()

  await screen.findByText("telegram")

  const testButtons = screen.getAllByRole("button", { name: /тест/i })
  await user.click(testButtons[0])

  expect(await screen.findByRole("alert")).toHaveTextContent("Канал не отвечает")
})

test("пустое состояние при отсутствии каналов", async () => {
  vi.spyOn(api, "listChannels").mockResolvedValue([])
  renderPage()

  expect(await screen.findByText(/каналов пока нет/i)).toBeInTheDocument()
})
