import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { describe, it, expect, vi, beforeEach } from 'vitest';

import { type InertiaFormProps, useForm } from '@inertiajs/react';

import { server } from '@/test/setup';

import PublishDraftModal from './PublishDraftModal';

// Mock useForm from Inertia
const mockPost = vi.fn();
const mockSetData = vi.fn();

// Create a mocked version of useForm
const mockedUseForm = vi.mocked(useForm);

type PublishFormData = { publish_type: 'draft' | 'publish'; wp_modified_at: string };

function mockFormReturn(
  overrides: Partial<InertiaFormProps<PublishFormData>> = {},
): InertiaFormProps<PublishFormData> {
  return {
    data: { publish_type: 'draft', wp_modified_at: '' },
    isDirty: false,
    errors: {},
    hasErrors: false,
    processing: false,
    progress: null,
    wasSuccessful: false,
    recentlySuccessful: false,
    setData: mockSetData,
    transform: vi.fn(),
    setDefaults: vi.fn() as InertiaFormProps<PublishFormData>['setDefaults'],
    reset: vi.fn(),
    clearErrors: vi.fn(),
    resetAndClearErrors: vi.fn(),
    setError: vi.fn() as InertiaFormProps<PublishFormData>['setError'],
    submit: vi.fn(),
    get: vi.fn(),
    patch: vi.fn(),
    post: mockPost,
    put: vi.fn(),
    delete: vi.fn(),
    cancel: vi.fn(),
    dontRemember: vi.fn() as InertiaFormProps<PublishFormData>['dontRemember'],
    withPrecognition: vi.fn() as unknown as InertiaFormProps<PublishFormData>['withPrecognition'],
    ...overrides,
  } as unknown as InertiaFormProps<PublishFormData>;
}

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    useForm: vi.fn(() => ({
      data: { publish_type: 'draft', wp_modified_at: '' },
      setData: mockSetData,
      post: mockPost,
      processing: false,
      errors: {},
    })),
  };
});

// Mock DiffPreview component
vi.mock('@/Components/Ai/DiffPreview', () => ({
  default: ({ originalContent, newContent }: { originalContent: string; newContent: string }) => (
    <div data-testid="diff-preview">
      <span data-testid="original-content">{originalContent}</span>
      <span data-testid="new-content">{newContent}</span>
    </div>
  ),
}));

// Mock route helper
global.route = vi.fn((name: string, params?: unknown) => {
  if (name === 'ai-drafts.conflict-check' && Array.isArray(params)) {
    return `/sites/${params[0]}/ai-drafts/${params[1]}/conflict-check`;
  }
  if (name === 'ai-drafts.publish' && Array.isArray(params)) {
    return `/sites/${params[0]}/ai-drafts/${params[1]}/publish`;
  }
  return '/';
}) as typeof route;

describe('PublishDraftModal', () => {
  const defaultProps = {
    open: true,
    onOpenChange: vi.fn(),
    siteId: 1,
    draftId: 42,
    wpPostId: '123',
    wpModifiedAt: '2026-01-15T12:00:00Z',
    originalContent: '<p>Original content</p>',
    newContent: '<p>New AI content</p>',
  };

  beforeEach(() => {
    vi.clearAllMocks();
    mockedUseForm.mockReturnValue(mockFormReturn());

    // Default MSW handler - no conflict
    server.use(
      http.get('/sites/:siteId/ai-drafts/:draftId/conflict-check', () => {
        return HttpResponse.json({
          has_conflict: false,
          wp_modified_at: '2026-01-15T12:00:00Z',
        });
      }),
    );
  });

  // ============================================
  // Rendering tests
  // ============================================

  describe('rendering', () => {
    it('renders the modal when open is true', () => {
      render(<PublishDraftModal {...defaultProps} />);

      expect(screen.getByText('Publish to WordPress')).toBeInTheDocument();
      expect(screen.getByText(/Review and publish your AI-generated content/i)).toBeInTheDocument();
    });

    it('displays WordPress post ID in description', () => {
      render(<PublishDraftModal {...defaultProps} />);

      expect(screen.getByText(/post #123/i)).toBeInTheDocument();
    });

    it('renders publish type select with both options', async () => {
      const user = userEvent.setup();
      render(<PublishDraftModal {...defaultProps} />);

      // The shadcn Select renders a trigger button — click to open the listbox
      const trigger = screen.getByRole('combobox');
      await user.click(trigger);

      expect(screen.getByRole('option', { name: /Save as WordPress Draft/i })).toBeInTheDocument();
      expect(screen.getByRole('option', { name: /Publish Immediately/i })).toBeInTheDocument();
    });

    it('selects draft as default publish type', () => {
      mockedUseForm.mockReturnValue(
        mockFormReturn({
          data: { publish_type: 'draft', wp_modified_at: '' },
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      // The shadcn Select trigger shows the selected value's label
      const trigger = screen.getByRole('combobox');
      expect(trigger).toBeInTheDocument();
      expect(trigger).toHaveTextContent(/Save as WordPress Draft/i);
    });

    it('renders Cancel and submit buttons', () => {
      render(<PublishDraftModal {...defaultProps} />);

      expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: 'Save as Draft' })).toBeInTheDocument();
    });

    it('shows loading state while checking for conflicts initially', () => {
      render(<PublishDraftModal {...defaultProps} />);

      // Conflict check is in-flight — DiffPreview not yet shown, no conflict message
      expect(screen.queryByTestId('diff-preview')).not.toBeInTheDocument();
      expect(screen.queryByText(/conflict detected/i)).not.toBeInTheDocument();
    });
  });

  // ============================================
  // Conflict check tests
  // ============================================

  describe('conflict check', () => {
    it('fetches conflict check when modal opens', async () => {
      const fetchSpy = vi.fn();
      server.use(
        http.get('/sites/:siteId/ai-drafts/:draftId/conflict-check', ({ request }) => {
          fetchSpy(request.url);
          return HttpResponse.json({
            has_conflict: false,
            wp_modified_at: '2026-01-15T12:00:00Z',
          });
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(fetchSpy).toHaveBeenCalledWith(
          expect.stringContaining('/sites/1/ai-drafts/42/conflict-check'),
        );
      });
    });

    it('displays DiffPreview after conflict check completes', async () => {
      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });

      // Loading state is gone once the conflict check resolves
    });

    it('displays conflict warning when conflict is detected', async () => {
      server.use(
        http.get('/sites/:siteId/ai-drafts/:draftId/conflict-check', () => {
          return HttpResponse.json({
            has_conflict: true,
            wp_modified_at: '2026-01-16T12:00:00Z',
          });
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByText(/The WordPress content has been modified/i)).toBeInTheDocument();
      });
    });

    it('does not display conflict warning when no conflict', async () => {
      server.use(
        http.get('/sites/:siteId/ai-drafts/:draftId/conflict-check', () => {
          return HttpResponse.json({
            has_conflict: false,
            wp_modified_at: '2026-01-15T12:00:00Z',
          });
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });

      expect(
        screen.queryByText(/The WordPress content has been modified/i),
      ).not.toBeInTheDocument();
    });

    it('handles conflict check error gracefully', async () => {
      server.use(
        http.get('/sites/:siteId/ai-drafts/:draftId/conflict-check', () => {
          return HttpResponse.error();
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });

      expect(
        screen.queryByText(/The WordPress content has been modified/i),
      ).not.toBeInTheDocument();
    });

    it('updates form data with latest wp_modified_at from conflict check', async () => {
      server.use(
        http.get('/sites/:siteId/ai-drafts/:draftId/conflict-check', () => {
          return HttpResponse.json({
            has_conflict: false,
            wp_modified_at: '2026-01-16T14:00:00Z',
          });
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(mockSetData).toHaveBeenCalledWith('wp_modified_at', '2026-01-16T14:00:00Z');
      });
    });

    it('resets conflict data when modal closes', async () => {
      const fetchSpy = vi.fn();
      server.use(
        http.get('/sites/:siteId/ai-drafts/:draftId/conflict-check', ({ request }) => {
          fetchSpy(request.url);
          return HttpResponse.json({
            has_conflict: false,
            wp_modified_at: '2026-01-15T12:00:00Z',
          });
        }),
      );

      const { rerender } = render(<PublishDraftModal {...defaultProps} />);

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

      fetchSpy.mockClear();

      // Close modal
      rerender(<PublishDraftModal {...defaultProps} open={false} />);

      // Reopen modal
      rerender(<PublishDraftModal {...defaultProps} open={true} />);

      await waitFor(() => {
        expect(fetchSpy).toHaveBeenCalledTimes(1);
      });
    });
  });

  // ============================================
  // Interaction tests
  // ============================================

  describe('interactions', () => {
    it('calls setData when publish type is changed', async () => {
      const user = userEvent.setup();
      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });

      // shadcn Select uses Radix UI — open trigger, then click option
      const trigger = screen.getByRole('combobox');
      await user.click(trigger);
      await user.click(screen.getByRole('option', { name: /Publish Immediately/i }));

      expect(mockSetData).toHaveBeenCalledWith('publish_type', 'publish');
    });

    it('changes submit button text when publish type is publish', async () => {
      mockedUseForm.mockReturnValue(
        mockFormReturn({
          data: { publish_type: 'publish', wp_modified_at: '' },
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByRole('button', { name: 'Publish Now' })).toBeInTheDocument();
      });
    });

    it('shows warning alert when publish type is publish', async () => {
      mockedUseForm.mockReturnValue(
        mockFormReturn({
          data: { publish_type: 'publish', wp_modified_at: '' },
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByText(/This will immediately publish the content/i)).toBeInTheDocument();
      });
    });

    it('calls onOpenChange(false) when Cancel button is clicked', async () => {
      const user = userEvent.setup();
      const onOpenChange = vi.fn();
      render(<PublishDraftModal {...defaultProps} onOpenChange={onOpenChange} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });

      const cancelButton = screen.getByRole('button', { name: 'Cancel' });
      await user.click(cancelButton);

      expect(onOpenChange).toHaveBeenCalledWith(false);
    });

    it('submits form with post request when submit button is clicked', async () => {
      const user = userEvent.setup();
      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });

      const submitButton = screen.getByRole('button', { name: 'Save as Draft' });
      await user.click(submitButton);

      expect(mockPost).toHaveBeenCalledWith(
        '/sites/1/ai-drafts/42/publish',
        expect.objectContaining({
          onSuccess: expect.any(Function),
        }),
      );
    });

    it('calls onOpenChange(false) after successful form submission', async () => {
      const user = userEvent.setup();
      const onOpenChange = vi.fn();

      mockPost.mockImplementation((url, options) => {
        if (options?.onSuccess) {
          options.onSuccess();
        }
      });

      render(<PublishDraftModal {...defaultProps} onOpenChange={onOpenChange} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });

      const submitButton = screen.getByRole('button', { name: 'Save as Draft' });
      await user.click(submitButton);

      await waitFor(() => {
        expect(onOpenChange).toHaveBeenCalledWith(false);
      });
    });
  });

  // ============================================
  // Error handling tests
  // ============================================

  describe('error handling', () => {
    it('displays validation error for publish_type', async () => {
      mockedUseForm.mockReturnValue(
        mockFormReturn({
          errors: {
            publish_type: 'The publish type field is required.',
          } as InertiaFormProps<PublishFormData>['errors'],
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByText('The publish type field is required.')).toBeInTheDocument();
      });
    });

    it('displays validation error for wp_modified_at', async () => {
      mockedUseForm.mockReturnValue(
        mockFormReturn({
          errors: {
            wp_modified_at: 'Content has been modified on WordPress.',
          } as InertiaFormProps<PublishFormData>['errors'],
        }),
      );

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByText('Content has been modified on WordPress.')).toBeInTheDocument();
      });
    });
  });

  // ============================================
  // Processing state tests
  // ============================================

  describe('processing state', () => {
    it('disables submit button when checking for conflicts', () => {
      render(<PublishDraftModal {...defaultProps} />);

      const submitButton = screen.getByRole('button', { name: 'Save as Draft' });
      expect(submitButton).toBeDisabled();
    });

    it('enables submit button after conflict check completes', async () => {
      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        const submitButton = screen.getByRole('button', { name: 'Save as Draft' });
        expect(submitButton).not.toBeDisabled();
      });
    });

    it('shows loading state when form is processing', async () => {
      mockedUseForm.mockReturnValue(mockFormReturn({ processing: true }));

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        const submitButton = screen.getByRole('button', { name: /Save as Draft|in progress/i });
        expect(submitButton).toBeDisabled();
      });
    });

    it('disables Cancel button when processing', async () => {
      mockedUseForm.mockReturnValue(mockFormReturn({ processing: true }));

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        const cancelButton = screen.getByRole('button', { name: 'Cancel' });
        expect(cancelButton).toBeDisabled();
      });
    });

    it('disables publish type select when processing', async () => {
      mockedUseForm.mockReturnValue(mockFormReturn({ processing: true }));

      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        const select = screen.getByLabelText('Publish Type');
        expect(select).toBeDisabled();
      });
    });
  });

  // ============================================
  // Modal behavior tests
  // ============================================

  describe('modal behavior', () => {
    it('does not render modal content when open is false', () => {
      render(<PublishDraftModal {...defaultProps} open={false} />);

      expect(screen.queryByText('Publish to WordPress')).not.toBeInTheDocument();
    });

    it('handles null wpModifiedAt gracefully', async () => {
      render(<PublishDraftModal {...defaultProps} wpModifiedAt={null} />);

      await waitFor(() => {
        expect(screen.getByTestId('diff-preview')).toBeInTheDocument();
      });
    });
  });

  // ============================================
  // Content preview tests
  // ============================================

  describe('content preview', () => {
    it('passes original content to DiffPreview', async () => {
      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByTestId('original-content')).toHaveTextContent('<p>Original content</p>');
      });
    });

    it('passes new content to DiffPreview', async () => {
      render(<PublishDraftModal {...defaultProps} />);

      await waitFor(() => {
        expect(screen.getByTestId('new-content')).toHaveTextContent('<p>New AI content</p>');
      });
    });
  });
});
