// src/pages/UserProfilePage.test.tsx
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
import UserProfilePage from './UserProfilePage';
import * as apiClient from '../services/apiClient';
import { UserProfile, Achievement, UserAchievement } from '../types';

// Mock dependencies
vi.mock('../services/apiClient'); // This was correct
vi.mock('../services/logger');
vi.mock('../services/notificationService');
vi.mock('../services/aiApiClient'); // Mock aiApiClient as it's used in the component
vi.mock('../components/AchievementsList', () => ({
  AchievementsList: ({ achievements }: { achievements: (UserAchievement & Achievement)[] }) => (
    <div data-testid="achievements-list-mock">
      Achievements Count: {achievements.length}
    </div>
  ),
}));

const mockedApiClient = apiClient as Mocked<typeof apiClient>;

// --- Mock Data ---
const mockProfile: UserProfile = {
  user_id: 'user-123',
  user: { user_id: 'user-123', email: 'test@example.com' },
  full_name: 'Test User',
  avatar_url: 'http://example.com/avatar.jpg',
  points: 150,
  role: 'user',
};

const mockAchievements: (UserAchievement & Achievement)[] = [
  {
    achievement_id: 1,
    user_id: 'user-123',
    achieved_at: '2024-01-01T00:00:00Z',
    name: 'First Steps',
    description: 'Uploaded first flyer.',
    icon: 'upload',
    points_value: 10,
  },
];

describe('UserProfilePage', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  // ... (Keep existing tests for loading message, error handling, rendering, etc.) ...

  it('should display a loading message initially', () => {
    mockedApiClient.getAuthenticatedUserProfile.mockReturnValue(new Promise(() => {}));
    mockedApiClient.getUserAchievements.mockReturnValue(new Promise(() => {}));
    render(<UserProfilePage />);
    expect(screen.getByText('Loading profile...')).toBeInTheDocument();
  });

  it('should display an error message if fetching profile fails', async () => {
    mockedApiClient.getAuthenticatedUserProfile.mockRejectedValue(new Error('Network Error'));
    mockedApiClient.getUserAchievements.mockResolvedValue(new Response(JSON.stringify(mockAchievements)));
    render(<UserProfilePage />);

    await waitFor(() => {
      expect(screen.getByText('Error: Network Error')).toBeInTheDocument();
    });
  });

  it('should display an error message if fetching achievements fails', async () => {
    mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(mockProfile)));
    mockedApiClient.getUserAchievements.mockRejectedValue(new Error('Achievements service down'));
    render(<UserProfilePage />);

    await waitFor(() => {
      expect(screen.getByText('Error: Achievements service down')).toBeInTheDocument();
    });
  });

  it('should render the profile and achievements on successful fetch', async () => {
    mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(mockProfile)));
    mockedApiClient.getUserAchievements.mockResolvedValue(new Response(JSON.stringify(mockAchievements)));
    render(<UserProfilePage />);

    await waitFor(() => {
      expect(screen.getByRole('heading', { name: 'Test User' })).toBeInTheDocument();
      expect(screen.getByText('test@example.com')).toBeInTheDocument();
      expect(screen.getByText('150 Points')).toBeInTheDocument();
      expect(screen.getByAltText('User Avatar')).toHaveAttribute('src', mockProfile.avatar_url);
      expect(screen.getByTestId('achievements-list-mock')).toHaveTextContent('Achievements Count: 1');
    });
  });

  it('should display a fallback avatar if the user has no avatar_url', async () => {
    // Create a mock profile with a null avatar_url and a specific name for the seed
    const profileWithoutAvatar = { ...mockProfile, avatar_url: null, full_name: 'No Avatar User' };
    mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(profileWithoutAvatar)));
    mockedApiClient.getUserAchievements.mockResolvedValue(new Response(JSON.stringify([])));

    render(<UserProfilePage />);

    // Wait for the component to render with the fetched data
    await waitFor(() => {
      const avatarImage = screen.getByAltText('User Avatar');
      // JSDOM might not URL-encode spaces in the src attribute in the same way a browser does.
      // We adjust the expectation to match the literal string returned by getAttribute.
      const expectedSrc = 'https://api.dicebear.com/8.x/initials/svg?seed=No Avatar User';
      console.log('[TEST LOG] Actual Avatar Src:', avatarImage.getAttribute('src'));
      expect(avatarImage).toHaveAttribute('src', expectedSrc);
    });
  });

  describe('Name Editing', () => {
    beforeEach(() => {
      mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(mockProfile)));
      mockedApiClient.getUserAchievements.mockResolvedValue(new Response(JSON.stringify(mockAchievements)));
    });

    it('should allow editing and saving the user name', async () => {
      const updatedProfile = { ...mockProfile, full_name: 'Updated Name' };
      mockedApiClient.updateUserProfile.mockResolvedValue(new Response(JSON.stringify(updatedProfile)));
      render(<UserProfilePage />);

      await screen.findByText('Test User');

      fireEvent.click(screen.getByRole('button', { name: /edit/i }));
      const nameInput = screen.getByRole('textbox');
      fireEvent.change(nameInput, { target: { value: 'Updated Name' } });
      fireEvent.click(screen.getByRole('button', { name: /save/i }));

      await waitFor(() => {
        expect(mockedApiClient.updateUserProfile).toHaveBeenCalledWith({ full_name: 'Updated Name' });
        expect(screen.getByRole('heading', { name: 'Updated Name' })).toBeInTheDocument();
      });
    });

    it('should allow canceling the name edit', async () => {
      render(<UserProfilePage />);
      await screen.findByText('Test User');

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

      expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
      expect(screen.getByRole('heading', { name: 'Test User' })).toBeInTheDocument();
    });
  });

  describe('Avatar Upload', () => {
    beforeEach(() => {
      mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(mockProfile)));
      mockedApiClient.getUserAchievements.mockResolvedValue(new Response(JSON.stringify(mockAchievements)));
    });

    it('should upload a new avatar and update the image source', async () => {
      const updatedProfile = { ...mockProfile, avatar_url: 'http://example.com/new-avatar.png' };
      
      // Log when the mock is called
      mockedApiClient.uploadAvatar.mockImplementation((file) => {
        console.log('[TEST LOG] uploadAvatar mock called with:', file.name);
        // Add a slight delay to ensure "isUploading" state can be observed
        return new Promise(resolve => {
          setTimeout(() => {
             console.log('[TEST LOG] uploadAvatar mock resolving...');
             resolve(new Response(JSON.stringify(updatedProfile)));
          }, 100);
        });
      });

      render(<UserProfilePage />);

      await screen.findByAltText('User Avatar');

      // Mock the hidden file input
      const fileInput = screen.getByTestId('avatar-file-input');
      const file = new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' });

      console.log('[TEST LOG] Firing file change event...');
      fireEvent.change(fileInput, { target: { files: [file] } });

      // DEBUG: Print current DOM state if spinner is not found immediately
      // const spinner = screen.queryByTestId('avatar-upload-spinner');
      // if (!spinner) {
      //   console.log('[TEST LOG] Spinner NOT found immediately after event.');
      //   // screen.debug(); // Uncomment to see DOM
      // } else {
      //   console.log('[TEST LOG] Spinner FOUND immediately.');
      // }

      // Wait for the spinner to appear
      console.log('[TEST LOG] Waiting for spinner...');
      await screen.findByTestId('avatar-upload-spinner');
      console.log('[TEST LOG] Spinner found.');

      // Wait for the upload to complete and the UI to update.
      await waitFor(() => {
        expect(mockedApiClient.uploadAvatar).toHaveBeenCalledWith(file);
        expect(screen.getByAltText('User Avatar')).toHaveAttribute('src', updatedProfile.avatar_url);
        expect(screen.queryByTestId('avatar-upload-spinner')).not.toBeInTheDocument();
      });
    });
  });
});