import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { UploadQuota } from './UploadQuota';
import { getUserQuota } from '../services/uploadService';
import { TrackServiceError as TrackUploadError } from '../errors/trackErrors';
import { useAuthStore } from '@/features/auth/store/authStore';

// Mock trackService
vi.mock('../services/trackService', () => ({
  getUserQuota: vi.fn(),
  TrackUploadError: class TrackUploadError extends Error {
    constructor(
      message: string,
      public code: string,
      public retryable: boolean = false,
    ) {
      super(message);
      this.name = 'TrackUploadError';
    }
  },
}));

// Mock useAuthStore
vi.mock('@/features/auth/store/authStore', () => ({
  useAuthStore: vi.fn(),
}));

describe('UploadQuota', () => {
  const mockQuota = {
    tracks_count: 5,
    tracks_limit: 1000,
    storage_used: 50 * 1024 * 1024, // 50MB
    storage_limit: 100 * 1024 * 1024 * 1024, // 100GB
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should render loading state initially', async () => {
    vi.mocked(getUserQuota).mockImplementation(() => new Promise(() => {})); // Never resolves

    render(<UploadQuota userId="me" />);

    expect(screen.getByText('Chargement du quota...')).toBeInTheDocument();
  });

  it('should display quota information when loaded', async () => {
    vi.mocked(getUserQuota).mockResolvedValue(mockQuota);

    render(<UploadQuota userId="me" />);

    await waitFor(() => {
      expect(screen.getByText("Quota d'upload")).toBeInTheDocument();
      expect(screen.getByText('Tracks')).toBeInTheDocument();
      expect(screen.getByText(/5 \/ 1000/)).toBeInTheDocument();
      expect(screen.getByText('Stockage')).toBeInTheDocument();
      expect(screen.getByText(/50 MB/)).toBeInTheDocument();
      expect(screen.getByText(/100 GB/)).toBeInTheDocument();
    });
  });

  it('should display tracks quota with progress bar', async () => {
    vi.mocked(getUserQuota).mockResolvedValue(mockQuota);

    render(<UploadQuota userId={123} />);

    await waitFor(() => {
      const progressBars = screen.getAllByRole('progressbar');
      expect(progressBars.length).toBeGreaterThan(0);
    });
  });

  it('should display storage quota with progress bar', async () => {
    vi.mocked(getUserQuota).mockResolvedValue(mockQuota);

    render(<UploadQuota userId={123} />);

    await waitFor(() => {
      const progressBars = screen.getAllByRole('progressbar');
      expect(progressBars.length).toBeGreaterThanOrEqual(2);
    });
  });

  it('should show warning when quota is near limit', async () => {
    const nearLimitQuota = {
      tracks_count: 950,
      tracks_limit: 1000,
      storage_used: 95 * 1024 * 1024 * 1024, // 95GB
      storage_limit: 100 * 1024 * 1024 * 1024, // 100GB
    };

    vi.mocked(getUserQuota).mockResolvedValue(nearLimitQuota);

    render(<UploadQuota userId="me" />);

    await waitFor(() => {
      expect(
        screen.getByText(/Votre quota d'upload approche de sa limite/),
      ).toBeInTheDocument();
    });
  });

  it('should show error state when quota is exceeded', async () => {
    const exceededQuota = {
      tracks_count: 1000,
      tracks_limit: 1000,
      storage_used: 100 * 1024 * 1024 * 1024, // 100GB
      storage_limit: 100 * 1024 * 1024 * 1024, // 100GB
    };

    vi.mocked(getUserQuota).mockResolvedValue(exceededQuota);

    render(<UploadQuota userId="me" />);

    await waitFor(() => {
      expect(screen.getByText('Limite de tracks atteinte')).toBeInTheDocument();
      expect(
        screen.getByText('Limite de stockage atteinte'),
      ).toBeInTheDocument();
    });
  });

  it('should display error message when API call fails', async () => {
    const error = new TrackUploadError('Failed to load quota', 'SERVER', false);
    vi.mocked(getUserQuota).mockRejectedValue(error);

    render(<UploadQuota userId="me" />);

    await waitFor(() => {
      expect(screen.getByText('Failed to load quota')).toBeInTheDocument();
    });
  });

  it('should call onQuotaUpdated when quota is loaded', async () => {
    const onQuotaUpdated = vi.fn();
    vi.mocked(getUserQuota).mockResolvedValue(mockQuota);

    render(<UploadQuota userId="me" onQuotaUpdated={onQuotaUpdated} />);

    await waitFor(() => {
      expect(onQuotaUpdated).toHaveBeenCalledWith(mockQuota);
    });
  });

  it('should reload quota when userId changes', async () => {
    vi.mocked(getUserQuota).mockResolvedValue(mockQuota);

    const { rerender } = render(<UploadQuota userId={123} />);

    await waitFor(() => {
      expect(getUserQuota).toHaveBeenCalledWith(123);
    });

    rerender(<UploadQuota userId={456} />);

    await waitFor(() => {
      expect(getUserQuota).toHaveBeenCalledWith(456);
    });
  });

  it('should format file sizes correctly', async () => {
    const smallQuota = {
      tracks_count: 0,
      tracks_limit: 1000,
      storage_used: 1024, // 1KB
      storage_limit: 1024 * 1024, // 1MB
    };

    vi.mocked(getUserQuota).mockResolvedValue(smallQuota);

    render(<UploadQuota userId="me" />);

    await waitFor(() => {
      expect(screen.getByText(/1 KB/)).toBeInTheDocument();
      expect(screen.getByText(/1 MB/)).toBeInTheDocument();
    });
  });

  it('should handle empty quota (no tracks)', async () => {
    const emptyQuota = {
      tracks_count: 0,
      tracks_limit: 1000,
      storage_used: 0,
      storage_limit: 100 * 1024 * 1024 * 1024,
    };

    vi.mocked(getUserQuota).mockResolvedValue(emptyQuota);

    render(<UploadQuota userId="me" />);

    await waitFor(() => {
      expect(screen.getByText(/0 \/ 1000/)).toBeInTheDocument();
      expect(screen.getByText(/0 Bytes/)).toBeInTheDocument();
    });
  });
});
