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

import { useForm } from '@inertiajs/react';

import Register from './Register';

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

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    useForm: vi.fn(() => ({
      data: { name: '', email: '', password: '', password_confirmation: '', remember: false },
      setData: mockSetData,
      post: mockPost,
      processing: false,
      errors: {},
      reset: mockReset,
    })),
    Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
      <a href={href}>{children}</a>
    ),
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    usePage: vi.fn(() => ({
      props: { auth: { user: null }, errors: {}, flash: {} },
    })),
  };
});

// Mock analytics
const mockTrackProductEvent = vi.fn();
const mockTrackEvent = vi.fn();
vi.mock('@/lib/analytics', () => ({
  trackProductEvent: (...args: unknown[]) => mockTrackProductEvent(...args),
  trackEvent: (...args: unknown[]) => mockTrackEvent(...args),
}));

// Mock the LegalContentModal
vi.mock('@/Components/legal/LegalContentModal', () => ({
  LegalContentModal: ({ type, onClose }: { type: string | null; onClose: () => void }) =>
    type ? (
      <div data-testid="legal-modal">
        <span data-testid="modal-type">{type}</span>
        <button onClick={onClose} data-testid="close-modal">
          Close
        </button>
      </div>
    ) : null,
}));

// Mock AuthLayout
vi.mock('@/Layouts/AuthLayout', () => ({
  default: ({ children, footer }: { children: React.ReactNode; footer?: React.ReactNode }) => (
    <div data-testid="auth-layout">
      {children}
      {footer && <footer data-testid="footer">{footer}</footer>}
    </div>
  ),
}));

const mockedUseForm = vi.mocked(useForm);

describe('Register Page', () => {
  const user = userEvent.setup();

  beforeEach(() => {
    vi.clearAllMocks();
    Object.defineProperty(window, 'location', {
      value: { href: 'http://localhost' },
      writable: true,
    });
    // Reset to default mock state with empty password (password strength hidden)
    mockedUseForm.mockReturnValue({
      data: { name: '', email: '', password: '', password_confirmation: '', remember: false },
      setData: mockSetData,
      post: mockPost,
      processing: false,
      errors: {},
      reset: mockReset,
    } as unknown as ReturnType<typeof useForm>);
  });

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

  describe('rendering', () => {
    it('renders the registration form with all required fields', () => {
      render(<Register />);

      expect(screen.getByLabelText(/email address/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument();
    });

    it('renders create account header', () => {
      render(<Register />);

      expect(screen.getByText(/create your account/i)).toBeInTheDocument();
      expect(
        screen.getByText(/connect your wordpress site and find seo wins/i),
      ).toBeInTheDocument();
    });

    it('renders sign in link', () => {
      render(<Register />);

      expect(screen.getByText(/already have an account/i)).toBeInTheDocument();
      expect(screen.getByText(/sign in instead/i)).toBeInTheDocument();
    });

    it('does not render terms checkbox (LAND-006: removed for reduced friction)', () => {
      render(<Register />);

      // Only the remember-me checkbox remains; explicit terms checkbox was removed
      const checkboxes = screen.getAllByRole('checkbox');
      expect(checkboxes.length).toBe(1);
      expect(screen.queryByRole('checkbox', { name: /i agree to the/i })).not.toBeInTheDocument();
    });

    it('renders remember me checkbox with default days', () => {
      render(<Register />);

      expect(screen.getByText(/keep me signed in for 30 days/i)).toBeInTheDocument();
    });

    it('renders remember me checkbox with custom days', () => {
      render(<Register rememberDays={14} />);

      expect(screen.getByText(/keep me signed in for 14 days/i)).toBeInTheDocument();
    });

    it('renders singular "day" for 1 day', () => {
      render(<Register rememberDays={1} />);

      expect(screen.getByText(/keep me signed in for 1 day$/i)).toBeInTheDocument();
      expect(screen.queryByText(/1 days/i)).not.toBeInTheDocument();
    });

    it('renders submit button', () => {
      render(<Register />);

      expect(screen.getByRole('button', { name: /start free/i })).toBeInTheDocument();
    });
  });

  // ============================================
  // Social authentication tests
  // ============================================

  describe('social authentication', () => {
    it('renders GitHub and Google buttons when socialAuth feature is enabled', () => {
      render(<Register features={{ socialAuth: true }} />);

      expect(screen.getByRole('button', { name: /github/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /google/i })).toBeInTheDocument();
    });

    it('does not render social buttons when feature is disabled', () => {
      render(<Register features={{ socialAuth: false }} />);

      expect(screen.queryByRole('button', { name: /github/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('button', { name: /google/i })).not.toBeInTheDocument();
    });

    it('shows "or register with email" separator when social auth is enabled', () => {
      render(<Register features={{ socialAuth: true }} />);

      expect(screen.getByText(/or register with email/i)).toBeInTheDocument();
    });

    it('does not show separator when social auth is disabled', () => {
      render(<Register features={{ socialAuth: false }} />);

      expect(screen.queryByText(/or register with email/i)).not.toBeInTheDocument();
    });

    it('redirects to GitHub auth on button click', async () => {
      render(<Register features={{ socialAuth: true }} />);

      const githubButton = screen.getByRole('button', { name: /github/i });
      await user.click(githubButton);

      expect(window.location.href).toContain('github');
    });

    it('redirects to Google auth on button click', async () => {
      render(<Register features={{ socialAuth: true }} />);

      const googleButton = screen.getByRole('button', { name: /google/i });
      await user.click(googleButton);

      expect(window.location.href).toContain('google');
    });

    it('shows "Redirecting..." text during social redirect', async () => {
      render(<Register features={{ socialAuth: true }} />);

      const githubButton = screen.getByRole('button', { name: /github/i });
      await user.click(githubButton);

      expect(screen.getByText(/redirecting/i)).toBeInTheDocument();
    });

    it('disables all social buttons during redirect', async () => {
      render(<Register features={{ socialAuth: true }} />);

      const githubButton = screen.getByRole('button', { name: /github/i });
      await user.click(githubButton);

      const googleButton = screen.getByRole('button', { name: /google/i });
      expect(googleButton).toBeDisabled();
    });
  });

  // ============================================
  // Password strength tests
  // ============================================

  describe('password strength', () => {
    it('shows password strength indicator when password is entered', () => {
      mockedUseForm.mockReturnValue({
        data: { name: '', email: '', password: 'test', password_confirmation: '', remember: false },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      expect(screen.getByText(/password strength/i)).toBeInTheDocument();
    });

    it('does not show password strength when password is empty', () => {
      render(<Register />);

      expect(screen.queryByText(/password strength/i)).not.toBeInTheDocument();
    });

    it('displays "Weak" for passwords under 25% strength', () => {
      mockedUseForm.mockReturnValue({
        data: { name: '', email: '', password: 'a', password_confirmation: '', remember: false },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      expect(screen.getByText('Weak')).toBeInTheDocument();
    });

    it('displays "Strong" for passwords meeting all requirements', () => {
      mockedUseForm.mockReturnValue({
        data: {
          name: '',
          email: '',
          password: 'Password1',
          password_confirmation: '',
          remember: false,
        },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      expect(screen.getByText('Strong')).toBeInTheDocument();
    });

    it('shows requirement items', () => {
      mockedUseForm.mockReturnValue({
        data: { name: '', email: '', password: 'test', password_confirmation: '', remember: false },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      expect(screen.getByText(/at least 8 characters/i)).toBeInTheDocument();
      expect(screen.getByText(/one uppercase letter/i)).toBeInTheDocument();
      expect(screen.getByText(/one lowercase letter/i)).toBeInTheDocument();
      expect(screen.getByText(/one number/i)).toBeInTheDocument();
    });
  });

  // ============================================
  // Terms and conditions tests
  // ============================================

  describe('terms and conditions', () => {
    it('submit button is enabled on load (validation fires on click)', () => {
      render(<Register />);

      // CRO-003: submit must be enabled on load so Zod fires on click and shows inline errors
      const submitButton = screen.getByRole('button', { name: /start free/i });
      expect(submitButton).not.toBeDisabled();
    });

    it('opens terms modal on Terms of Service link click', async () => {
      render(<Register />);

      const termsLinks = screen.getAllByText(/terms of service/i);
      await user.click(termsLinks[0]);

      expect(screen.getByTestId('legal-modal')).toBeInTheDocument();
      expect(screen.getByTestId('modal-type')).toHaveTextContent('terms');
    });

    it('opens privacy modal on Privacy Policy link click', async () => {
      render(<Register />);

      const privacyLink = screen.getByText(/privacy policy/i);
      await user.click(privacyLink);

      expect(screen.getByTestId('legal-modal')).toBeInTheDocument();
      expect(screen.getByTestId('modal-type')).toHaveTextContent('privacy');
    });

    it('closes legal modal on close button click', async () => {
      render(<Register />);

      const termsLinks = screen.getAllByText(/terms of service/i);
      await user.click(termsLinks[0]);

      expect(screen.getByTestId('legal-modal')).toBeInTheDocument();

      await user.click(screen.getByTestId('close-modal'));

      expect(screen.queryByTestId('legal-modal')).not.toBeInTheDocument();
    });
  });

  // ============================================
  // Client-side validation tests
  // ============================================

  describe('client-side validation', () => {
    it('shows email required error on blur with empty email', async () => {
      render(<Register />);

      const emailInput = screen.getByLabelText(/email address/i);
      await user.click(emailInput);
      await user.tab(); // blur

      await waitFor(() => {
        expect(screen.getByText(/email is required/i)).toBeInTheDocument();
      });
    });

    it('shows invalid email error for malformed email', async () => {
      render(<Register />);

      const emailInput = screen.getByLabelText(/email address/i) as HTMLInputElement;
      emailInput.value = 'notanemail';
      fireEvent.blur(emailInput);

      await waitFor(() => {
        expect(screen.getByText(/please enter a valid email address/i)).toBeInTheDocument();
      });
    });

    it('shows password min length error on blur with short password', async () => {
      render(<Register />);

      const passwordInput = screen.getByLabelText(/^password$/i) as HTMLInputElement;
      passwordInput.value = 'short';
      fireEvent.blur(passwordInput);

      await waitFor(() => {
        expect(screen.getByText(/password must be at least 8 characters/i)).toBeInTheDocument();
      });
    });

    it('prevents form submission with validation errors', async () => {
      render(<Register />);

      // Accept terms and set strong password so button is not disabled by those checks
      mockedUseForm.mockReturnValue({
        data: {
          name: '',
          email: '',
          password: 'Password1',
          password_confirmation: 'Password1',
          remember: false,
        },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      // Re-render with valid password (enables submit button)
      const { unmount } = render(<Register />);

      const submitButton = screen.getAllByRole('button', { name: /start free/i })[0];
      await user.click(submitButton);

      // post should not be called due to empty name/email failing validation
      expect(mockPost).not.toHaveBeenCalled();

      unmount();
    });
  });

  // ============================================
  // Form submission tests
  // ============================================

  describe('form submission', () => {
    it('calls setData when email changes', async () => {
      render(<Register />);

      const emailInput = screen.getByLabelText(/email address/i);
      await user.type(emailInput, 'test@example.com');

      expect(mockSetData).toHaveBeenCalledWith('email', expect.any(String));
    });

    it('calls setData when password changes', async () => {
      render(<Register />);

      const passwordInput = screen.getByLabelText(/^password$/i);
      await user.type(passwordInput, 'Password123');

      expect(mockSetData).toHaveBeenCalledWith('password', expect.any(String));
    });

    it('calls post with register route on valid submission', async () => {
      mockedUseForm.mockReturnValue({
        data: {
          name: 'Test',
          email: 'test@test.com',
          password: 'Password1',
          password_confirmation: 'Password1',
          remember: false,
        },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

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

      expect(mockPost).toHaveBeenCalled();
    });
  });

  // ============================================
  // Password visibility tests
  // ============================================

  describe('password visibility', () => {
    it('password field starts as password type', () => {
      render(<Register />);

      const passwordInput = screen.getByLabelText(/^password$/i);
      expect(passwordInput).toHaveAttribute('type', 'password');
    });

    it('toggles password visibility on button click', async () => {
      render(<Register />);

      const toggleButton = screen.getByRole('button', { name: /show password/i });
      const passwordInput = screen.getByLabelText(/^password$/i);

      expect(passwordInput).toHaveAttribute('type', 'password');

      await user.click(toggleButton);

      expect(passwordInput).toHaveAttribute('type', 'text');
      expect(screen.getByRole('button', { name: /hide password/i })).toBeInTheDocument();
    });
  });

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

  describe('processing state', () => {
    it('shows "Creating account..." when processing', () => {
      mockedUseForm.mockReturnValue({
        data: {
          name: '',
          email: '',
          password: 'Password1',
          password_confirmation: '',
          remember: false,
        },
        setData: mockSetData,
        post: mockPost,
        processing: true,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      expect(
        screen.getByRole('button', { name: /creating account|in progress/i }),
      ).toBeInTheDocument();
    });

    it('disables submit button when processing', () => {
      mockedUseForm.mockReturnValue({
        data: {
          name: '',
          email: '',
          password: 'Password1',
          password_confirmation: '',
          remember: false,
        },
        setData: mockSetData,
        post: mockPost,
        processing: true,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      const submitButton = screen.getByRole('button', { name: /creating account|in progress/i });
      expect(submitButton).toBeDisabled();
    });

    it('disables social buttons when form is processing', () => {
      mockedUseForm.mockReturnValue({
        data: { name: '', email: '', password: '', password_confirmation: '', remember: false },
        setData: mockSetData,
        post: mockPost,
        processing: true,
        errors: {},
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register features={{ socialAuth: true }} />);

      const githubButton = screen.getByRole('button', { name: /github/i });
      const googleButton = screen.getByRole('button', { name: /google/i });

      // Social buttons are now disabled when form is processing
      expect(githubButton).toBeDisabled();
      expect(googleButton).toBeDisabled();
    });
  });

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

  describe('error handling', () => {
    it('displays error message when error prop is provided', () => {
      render(<Register error="An error occurred" />);

      expect(screen.getByText(/an error occurred/i)).toBeInTheDocument();
    });

    it('displays server email error', () => {
      mockedUseForm.mockReturnValue({
        data: { name: '', email: '', password: '', password_confirmation: '', remember: false },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: { email: 'The email has already been taken.' },
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      expect(screen.getByText(/the email has already been taken/i)).toBeInTheDocument();
    });

    it('displays server password error', () => {
      mockedUseForm.mockReturnValue({
        data: { name: '', email: '', password: '', password_confirmation: '', remember: false },
        setData: mockSetData,
        post: mockPost,
        processing: false,
        errors: { password: 'The password must be at least 8 characters.' },
        reset: mockReset,
      } as unknown as ReturnType<typeof useForm>);

      render(<Register />);

      expect(screen.getByText(/the password must be at least 8 characters/i)).toBeInTheDocument();
    });

  });

  // ============================================
  // Accessibility tests
  // ============================================

  describe('accessibility', () => {
    it('has proper labels for all form fields', () => {
      render(<Register />);

      expect(screen.getByLabelText(/email address/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument();
    });

    it('has proper autocomplete attributes', () => {
      render(<Register />);

      expect(screen.getByLabelText(/email address/i)).toHaveAttribute('autocomplete', 'username');
      expect(screen.getByLabelText(/^password$/i)).toHaveAttribute('autocomplete', 'new-password');
    });

    it('toggle button has aria-label', () => {
      render(<Register />);

      const toggleButton = screen.getByRole('button', { name: /show password/i });
      expect(toggleButton).toHaveAttribute('aria-label');
    });
  });

  // ============================================
  // Analytics tracking tests
  // ============================================

  describe('analytics tracking', () => {
    it('fires REGISTER_PAGE_VIEWED on mount (ANA-018)', () => {
      render(<Register />);

      expect(mockTrackProductEvent).toHaveBeenCalledWith('register_page_viewed');
    });

    it('fires REGISTER_PAGE_VIEWED exactly once on mount', () => {
      render(<Register />);

      const registerPageViewedCalls = mockTrackProductEvent.mock.calls.filter(
        ([event]) => event === 'register_page_viewed',
      );
      expect(registerPageViewedCalls).toHaveLength(1);
    });
  });
});
