import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ProfileEditForm } from './ProfileEditForm';
import * as profileService from '../services/profileService';

// Mock ResizeObserver for tests
global.ResizeObserver = vi.fn().mockImplementation(() => ({
  observe: vi.fn(),
  unobserve: vi.fn(),
  disconnect: vi.fn(),
}));

// Mock profileService
vi.mock('../services/profileService', () => ({
  updateProfile: vi.fn(),
}));

// Mock useAuthStore
vi.mock('@/features/auth/store/authStore', () => ({
  useAuthStore: () => ({
    user: {
      id: 123,
      username: 'testuser',
      email: 'test@example.com',
      first_name: 'Test',
      last_name: 'User',
    },
  }),
}));

const createTestQueryClient = () =>
  new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

const TestWrapper = ({ children }: { children: React.ReactNode }) => {
  const queryClient = createTestQueryClient();
  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>{children}</BrowserRouter>
    </QueryClientProvider>
  );
};

describe('ProfileEditForm Component', () => {
  const mockOnSuccess = vi.fn();
  const mockOnCancel = vi.fn();

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(profileService.updateProfile).mockResolvedValue({
      id: 123,
      username: 'testuser',
      first_name: 'Test',
      last_name: 'User',
      avatar_url: null,
      bio: 'Test bio',
      location: null,
      birthdate: null,
      gender: null,
      created_at: '2024-01-01T00:00:00Z',
    });
  });

  it('renders form correctly with initial data', () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{
            first_name: 'Test',
            last_name: 'User',
            username: 'testuser',
            bio: 'Test bio',
          }}
          onSuccess={mockOnSuccess}
          onCancel={mockOnCancel}
        />
      </TestWrapper>,
    );

    expect(screen.getByLabelText(/first name/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/last name/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/username/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/bio/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/location/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/birthdate/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/gender/i)).toBeInTheDocument();
  });

  it('validates username format', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ username: 'testuser' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const usernameInput = screen.getByLabelText(/username/i);
    const submitButton = screen.getByRole('button', { name: /save profile/i });

    fireEvent.change(usernameInput, { target: { value: 'ab' } });
    fireEvent.click(submitButton);

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

  it('validates username alphanumeric and underscore only', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ username: 'testuser' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const usernameInput = screen.getByLabelText(/username/i);
    const submitButton = screen.getByRole('button', { name: /save profile/i });

    fireEvent.change(usernameInput, { target: { value: 'test-user!' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText(/can only contain letters, numbers, and underscores/i),
      ).toBeInTheDocument();
    });
  });

  it('validates bio max length', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm initialData={{ bio: '' }} onSuccess={mockOnSuccess} />
      </TestWrapper>,
    );

    const bioInput = screen.getByLabelText(/bio/i);
    const submitButton = screen.getByRole('button', { name: /save profile/i });

    fireEvent.change(bioInput, { target: { value: 'a'.repeat(501) } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText(/must be at most 500 characters/i),
      ).toBeInTheDocument();
    });
  });

  it('validates birthdate format', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ birthdate: '' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const birthdateInput = screen.getByLabelText(/birthdate/i);
    const submitButton = screen.getByRole('button', { name: /save profile/i });

    fireEvent.change(birthdateInput, { target: { value: 'invalid-date' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText(/invalid date format/i)).toBeInTheDocument();
    });
  });

  it('validates age requirement (must be at least 13 years old)', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ birthdate: '' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const birthdateInput = screen.getByLabelText(/birthdate/i);
    const submitButton = screen.getByRole('button', { name: /save profile/i });

    // Set birthdate to 10 years ago (less than 13)
    const tenYearsAgo = new Date();
    tenYearsAgo.setFullYear(tenYearsAgo.getFullYear() - 10);
    const dateString = tenYearsAgo.toISOString().split('T')[0];

    fireEvent.change(birthdateInput, { target: { value: dateString } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText(/must be at least 13 years old/i),
      ).toBeInTheDocument();
    });
  });

  it('submits form with valid data', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{
            first_name: 'Test',
            last_name: 'User',
            username: 'testuser',
          }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const firstNameInput = screen.getByLabelText(/first name/i);
    const submitButton = screen.getByRole('button', { name: /save profile/i });

    fireEvent.change(firstNameInput, { target: { value: 'Updated' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(profileService.updateProfile).toHaveBeenCalledWith(
        123,
        expect.objectContaining({
          first_name: 'Updated',
        }),
      );
    });
  });

  it('calls onSuccess callback after successful submission', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ username: 'testuser' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const submitButton = screen.getByRole('button', { name: /save profile/i });
    fireEvent.click(submitButton);

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

    await waitFor(
      () => {
        expect(mockOnSuccess).toHaveBeenCalled();
      },
      { timeout: 2000 },
    );
  });

  it('calls onCancel when cancel button is clicked', () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ username: 'testuser' }}
          onSuccess={mockOnSuccess}
          onCancel={mockOnCancel}
        />
      </TestWrapper>,
    );

    const cancelButton = screen.getByRole('button', { name: /cancel/i });
    fireEvent.click(cancelButton);

    expect(mockOnCancel).toHaveBeenCalled();
  });

  it('displays error message when update fails', async () => {
    vi.mocked(profileService.updateProfile).mockRejectedValue({
      response: {
        data: {
          error: 'Username already taken',
        },
      },
    });

    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ username: 'testuser' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const submitButton = screen.getByRole('button', { name: /save profile/i });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText(/username already taken/i)).toBeInTheDocument();
    });
  });

  it('displays success message after successful update', async () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ username: 'testuser' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const submitButton = screen.getByRole('button', { name: /save profile/i });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText(/profile updated successfully/i),
      ).toBeInTheDocument();
    });
  });

  it('shows character count for bio', () => {
    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ bio: 'Test' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const bioInput = screen.getByLabelText(/bio/i);
    fireEvent.change(bioInput, { target: { value: 'Test bio content' } });

    expect(screen.getByText(/17\/500 characters/i)).toBeInTheDocument();
  });

  it('disables submit button while submitting', async () => {
    vi.mocked(profileService.updateProfile).mockImplementation(
      () => new Promise((resolve) => setTimeout(resolve, 100)),
    );

    render(
      <TestWrapper>
        <ProfileEditForm
          initialData={{ username: 'testuser' }}
          onSuccess={mockOnSuccess}
        />
      </TestWrapper>,
    );

    const submitButton = screen.getByRole('button', { name: /save profile/i });
    fireEvent.click(submitButton);

    expect(screen.getByRole('button', { name: /saving/i })).toBeDisabled();

    await waitFor(() => {
      expect(
        screen.getByRole('button', { name: /save profile/i }),
      ).not.toBeDisabled();
    });
  });
});
