import React from 'react';
import { describe, it, expect, vi } from 'vitest';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';

import { CreatePost } from '@/pages/CreatePost/CreatePost';
import notificationsReducer from '@/state/features/notificationsSlice';
import authReducer, { addUser } from '@/state/features/authSlice';
import globalReducer, { setCurrentBlog } from '@/state/features/globalSlice';
import blogReducer from '@/state/features/blogSlice';
import mailReducer from '@/state/features/mailSlice';
import bookmarksReducer from '@/state/features/bookmarksSlice';
import { server } from '../msw/server';

const createTestStore = () =>
  configureStore({
    reducer: {
      notifications: notificationsReducer,
      auth: authReducer,
      global: globalReducer,
      blog: blogReducer,
      mail: mailReducer,
      bookmarks: bookmarksReducer,
    },
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware({
        serializableCheck: false,
      }),
  });

type TestStore = ReturnType<typeof createTestStore>;

const renderCreatePost = (store: TestStore) =>
  render(
    <Provider store={store}>
      <ThemeProvider theme={createTheme()}>
        <MemoryRouter initialEntries={[`/post/new`]}>
          <Routes>
            <Route path="/post/new" element={<CreatePost />} />
          </Routes>
        </MemoryRouter>
      </ThemeProvider>
    </Provider>,
  );

const selectMinimalEditor = async () => {
  const user = userEvent.setup();
  const option = await screen.findByText('Minimal Editor');
  await user.click(option);
  await screen.findByPlaceholderText('Title');
  return user;
};

describe('CreatePost (new publish flow)', () => {
  const categoriesHandler = http.get('/arbitrary/categories', () => HttpResponse.json([]));

  it('surfaces an error when no blog is selected', async () => {
    server.use(categoriesHandler);
    const store = createTestStore();
    globalThis.qortalRequest = vi.fn().mockResolvedValue({ ok: true });
    store.dispatch(addUser({ address: 'QADDR', publicKey: 'PUB', name: 'alice' }));

    renderCreatePost(store);
    const user = await selectMinimalEditor();

    const titleField = await screen.findByPlaceholderText('Title');
    await user.type(titleField, 'Unsaved Draft');

    const publishTrigger = await screen.findByRole('button', { name: /^Publish$/i });
    await user.click(publishTrigger);

    const submitButton = await screen.findByRole('button', { name: /Submit/i });
    await user.click(submitButton);

    await waitFor(() => {
      expect(store.getState().notifications.alertTypes.alertError).toBe(
        'Cannot determine which blog to publish to. Open your blog and try again.',
      );
    });
    expect(globalThis.qortalRequest).not.toHaveBeenCalled();
  }, 10000);

  it('publishes a new post when currentBlog is available', async () => {
    server.use(categoriesHandler);
    const store = createTestStore();
    globalThis.qortalRequest = vi.fn().mockResolvedValue({ ok: true });
    store.dispatch(addUser({ address: 'QADDR', publicKey: 'PUB', name: 'alice' }));
    store.dispatch(
      setCurrentBlog({
        createdAt: Date.now(),
        blogId: 'q-blog-myblog',
        title: 'My Blog',
        description: '',
        blogImage: '',
        category: '',
        tags: [],
        wikiEnabled: false,
        editorWhitelist: [],
        editorBlacklist: [],
      }),
    );

    renderCreatePost(store);
    const user = await selectMinimalEditor();

    const titleField = await screen.findByPlaceholderText('Title');
    await user.type(titleField, 'My New Post');

    const publishTrigger = await screen.findByRole('button', { name: /^Publish$/i });
    await user.click(publishTrigger);

    const submitButton = await screen.findByRole('button', { name: /Submit/i });
    await user.click(submitButton);

    await waitFor(() => {
      expect(globalThis.qortalRequest).toHaveBeenCalled();
    });

    const publishCall = (globalThis.qortalRequest as any).mock.calls.find(
      (call: any[]) => call[0]?.action === 'PUBLISH_QDN_RESOURCE',
    );
    expect(publishCall).toBeTruthy();
    expect(publishCall?.[0]).toMatchObject({
      action: 'PUBLISH_QDN_RESOURCE',
      service: 'BLOG_POST',
    });
    expect(publishCall?.[0]?.identifier.startsWith('q-blog-myblog-post-')).toBe(true);
    await waitFor(() => {
      expect(store.getState().notifications.alertTypes.alertSuccess).toBe(
        'Blog post successfully published',
      );
    });
  }, 10000);
});
