/**
 * Unit tests for MetricWidget component
 *
 * Tests cover:
 * - Component rendering with title and value
 * - Growth/trend indicators (positive, negative, neutral)
 * - Change percentage formatting
 * - Weekly and monthly metrics display
 * - Icon rendering
 * - Edit mode with drag handle and remove button
 * - Internationalization (i18n)
 * - Accessibility
 */

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

// Mock react-i18next
vi.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: (key: string) => {
      const translations: Record<string, string> = {
        'dashboard.weekLabel': 'Week:',
        'dashboard.monthLabel': 'Month:',
      };
      return translations[key] || key;
    },
  }),
}));

describe('MetricWidget', () => {
  const mockGrowthData = {
    weekly: { value: 100, change: 5.5 },
    monthly: { value: 400, change: -2.3 },
  };

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

  describe('Rendering', () => {
    it('should render the component', () => {
      render(
        <MetricWidget
          title="Total Revenue"
          value="$12,345"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('Total Revenue')).toBeInTheDocument();
    });

    it('should render title correctly', () => {
      render(
        <MetricWidget
          title="Total Customers"
          value={150}
          growth={mockGrowthData}
        />
      );

      const title = screen.getByText('Total Customers');
      expect(title).toBeInTheDocument();
      expect(title).toHaveClass('text-sm', 'font-medium', 'text-gray-500');
    });

    it('should render numeric value', () => {
      render(
        <MetricWidget
          title="Total Appointments"
          value={42}
          growth={mockGrowthData}
        />
      );

      const value = screen.getByText('42');
      expect(value).toBeInTheDocument();
      expect(value).toHaveClass('text-2xl', 'font-bold', 'text-gray-900');
    });

    it('should render string value', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$25,000"
          growth={mockGrowthData}
        />
      );

      const value = screen.getByText('$25,000');
      expect(value).toBeInTheDocument();
    });

    it('should render with custom icon', () => {
      const CustomIcon = () => <span data-testid="custom-icon">💰</span>;

      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          icon={<CustomIcon />}
        />
      );

      expect(screen.getByTestId('custom-icon')).toBeInTheDocument();
    });

    it('should render without icon', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      const iconContainer = container.querySelector('.text-brand-500');
      expect(iconContainer).not.toBeInTheDocument();
    });
  });

  describe('Trend Indicators', () => {
    describe('Positive Change', () => {
      it('should show positive trend icon for weekly growth', () => {
        const positiveGrowth = {
          weekly: { value: 100, change: 10.5 },
          monthly: { value: 400, change: 0 },
        };

        const { container } = render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={positiveGrowth}
          />
        );

        const changeText = screen.getByText('+10.5%');
        expect(changeText).toBeInTheDocument();

        // Check for TrendingUp icon (lucide-react renders as SVG)
        const svgs = container.querySelectorAll('svg');
        expect(svgs.length).toBeGreaterThan(0);
      });

      it('should apply positive change styling', () => {
        const positiveGrowth = {
          weekly: { value: 100, change: 15 },
          monthly: { value: 400, change: 0 },
        };

        render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={positiveGrowth}
          />
        );

        const changeElement = screen.getByText('+15.0%').closest('span');
        expect(changeElement).toHaveClass('text-green-700', 'bg-green-50');
      });

      it('should format positive change with plus sign', () => {
        const positiveGrowth = {
          weekly: { value: 100, change: 7.8 },
          monthly: { value: 400, change: 3.2 },
        };

        render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={positiveGrowth}
          />
        );

        expect(screen.getByText('+7.8%')).toBeInTheDocument();
        expect(screen.getByText('+3.2%')).toBeInTheDocument();
      });
    });

    describe('Negative Change', () => {
      it('should show negative trend icon for monthly growth', () => {
        const negativeGrowth = {
          weekly: { value: 100, change: 0 },
          monthly: { value: 400, change: -5.5 },
        };

        const { container } = render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={negativeGrowth}
          />
        );

        const changeText = screen.getByText('-5.5%');
        expect(changeText).toBeInTheDocument();

        // Check for TrendingDown icon
        const svgs = container.querySelectorAll('svg');
        expect(svgs.length).toBeGreaterThan(0);
      });

      it('should apply negative change styling', () => {
        const negativeGrowth = {
          weekly: { value: 100, change: -12.3 },
          monthly: { value: 400, change: 0 },
        };

        render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={negativeGrowth}
          />
        );

        const changeElement = screen.getByText('-12.3%').closest('span');
        expect(changeElement).toHaveClass('text-red-700', 'bg-red-50');
      });

      it('should format negative change without extra minus sign', () => {
        const negativeGrowth = {
          weekly: { value: 100, change: -8.9 },
          monthly: { value: 400, change: -15.2 },
        };

        render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={negativeGrowth}
          />
        );

        expect(screen.getByText('-8.9%')).toBeInTheDocument();
        expect(screen.getByText('-15.2%')).toBeInTheDocument();
      });
    });

    describe('Zero Change', () => {
      it('should show neutral trend icon for zero change', () => {
        const zeroGrowth = {
          weekly: { value: 100, change: 0 },
          monthly: { value: 400, change: 0 },
        };

        const { container } = render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={zeroGrowth}
          />
        );

        const changeTexts = screen.getAllByText('0%');
        expect(changeTexts).toHaveLength(2);

        // Check for Minus icon
        const svgs = container.querySelectorAll('svg');
        expect(svgs.length).toBeGreaterThan(0);
      });

      it('should apply neutral change styling', () => {
        const zeroGrowth = {
          weekly: { value: 100, change: 0 },
          monthly: { value: 400, change: 0 },
        };

        render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={zeroGrowth}
          />
        );

        const changeElements = screen.getAllByText('0%');
        changeElements.forEach((element) => {
          const spanElement = element.closest('span');
          expect(spanElement).toHaveClass('text-gray-700', 'bg-gray-50');
        });
      });

      it('should format zero change as 0%', () => {
        const zeroGrowth = {
          weekly: { value: 100, change: 0 },
          monthly: { value: 400, change: 0 },
        };

        render(
          <MetricWidget
            title="Revenue"
            value="$1000"
            growth={zeroGrowth}
          />
        );

        const changeTexts = screen.getAllByText('0%');
        expect(changeTexts).toHaveLength(2);
      });
    });
  });

  describe('Weekly and Monthly Metrics', () => {
    it('should display weekly label', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('Week:')).toBeInTheDocument();
    });

    it('should display monthly label', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('Month:')).toBeInTheDocument();
    });

    it('should display weekly change percentage', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('+5.5%')).toBeInTheDocument();
    });

    it('should display monthly change percentage', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('-2.3%')).toBeInTheDocument();
    });

    it('should handle different weekly and monthly trends', () => {
      const mixedGrowth = {
        weekly: { value: 100, change: 12.5 },
        monthly: { value: 400, change: -8.2 },
      };

      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mixedGrowth}
        />
      );

      expect(screen.getByText('+12.5%')).toBeInTheDocument();
      expect(screen.getByText('-8.2%')).toBeInTheDocument();
    });

    it('should format change values to one decimal place', () => {
      const preciseGrowth = {
        weekly: { value: 100, change: 5.456 },
        monthly: { value: 400, change: -3.789 },
      };

      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={preciseGrowth}
        />
      );

      expect(screen.getByText('+5.5%')).toBeInTheDocument();
      expect(screen.getByText('-3.8%')).toBeInTheDocument();
    });
  });

  describe('Edit Mode', () => {
    it('should not show edit controls when isEditing is false', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          isEditing={false}
        />
      );

      const dragHandle = container.querySelector('.drag-handle');
      expect(dragHandle).not.toBeInTheDocument();
    });

    it('should show drag handle when in edit mode', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          isEditing={true}
        />
      );

      const dragHandle = container.querySelector('.drag-handle');
      expect(dragHandle).toBeInTheDocument();
    });

    it('should show remove button when in edit mode', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          isEditing={true}
          onRemove={vi.fn()}
        />
      );

      const removeButton = screen.getByRole('button');
      expect(removeButton).toBeInTheDocument();
    });

    it('should call onRemove when remove button is clicked', async () => {
      const user = userEvent.setup();
      const handleRemove = vi.fn();

      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          isEditing={true}
          onRemove={handleRemove}
        />
      );

      const removeButton = screen.getByRole('button');
      await user.click(removeButton);

      expect(handleRemove).toHaveBeenCalledTimes(1);
    });

    it('should apply padding when in edit mode', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          isEditing={true}
        />
      );

      const contentContainer = container.querySelector('.pl-5');
      expect(contentContainer).toBeInTheDocument();
    });

    it('should not apply padding when not in edit mode', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          isEditing={false}
        />
      );

      const contentContainer = container.querySelector('.pl-5');
      expect(contentContainer).not.toBeInTheDocument();
    });
  });

  describe('Styling', () => {
    it('should apply container styles', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      const widget = container.firstChild;
      expect(widget).toHaveClass(
        'h-full',
        'p-4',
        'bg-white',
        'rounded-xl',
        'border',
        'border-gray-200',
        'shadow-sm',
        'relative',
        'group'
      );
    });

    it('should apply dark mode styles', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      const widget = container.firstChild;
      expect(widget).toHaveClass('dark:bg-gray-800', 'dark:border-gray-700');
    });

    it('should apply trend badge styles', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      const badges = container.querySelectorAll('.rounded-full');
      expect(badges.length).toBeGreaterThan(0);
    });
  });

  describe('Accessibility', () => {
    it('should have semantic HTML structure', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      const paragraphs = container.querySelectorAll('p');
      const divs = container.querySelectorAll('div');

      expect(paragraphs.length).toBeGreaterThan(0);
      expect(divs.length).toBeGreaterThan(0);
    });

    it('should have readable text contrast', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      const title = screen.getByText('Revenue');
      expect(title).toHaveClass('text-gray-500');
    });

    it('should make remove button accessible when in edit mode', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
          isEditing={true}
          onRemove={vi.fn()}
        />
      );

      const button = screen.getByRole('button');
      expect(button).toBeInTheDocument();
    });
  });

  describe('Internationalization', () => {
    it('should use translation for week label', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('Week:')).toBeInTheDocument();
    });

    it('should use translation for month label', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('Month:')).toBeInTheDocument();
    });
  });

  describe('Integration', () => {
    it('should render correctly with all props', () => {
      const CustomIcon = () => <span data-testid="icon">📊</span>;
      const handleRemove = vi.fn();
      const fullGrowth = {
        weekly: { value: 150, change: 10 },
        monthly: { value: 600, change: -5 },
      };

      render(
        <MetricWidget
          title="Total Revenue"
          value="$15,000"
          growth={fullGrowth}
          icon={<CustomIcon />}
          isEditing={true}
          onRemove={handleRemove}
        />
      );

      expect(screen.getByText('Total Revenue')).toBeInTheDocument();
      expect(screen.getByText('$15,000')).toBeInTheDocument();
      expect(screen.getByTestId('icon')).toBeInTheDocument();
      expect(screen.getByText('+10.0%')).toBeInTheDocument();
      expect(screen.getByText('-5.0%')).toBeInTheDocument();
      expect(screen.getByRole('button')).toBeInTheDocument();
    });

    it('should handle edge case values', () => {
      const edgeCaseGrowth = {
        weekly: { value: 0, change: 0 },
        monthly: { value: 1000000, change: 99.9 },
      };

      render(
        <MetricWidget
          title="Edge Case"
          value={0}
          growth={edgeCaseGrowth}
        />
      );

      expect(screen.getByText('Edge Case')).toBeInTheDocument();
      expect(screen.getByText('0')).toBeInTheDocument();
      expect(screen.getByText('0%')).toBeInTheDocument();
      expect(screen.getByText('+99.9%')).toBeInTheDocument();
    });

    it('should maintain layout with long titles', () => {
      render(
        <MetricWidget
          title="Very Long Metric Title That Should Still Display Properly"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      const title = screen.getByText('Very Long Metric Title That Should Still Display Properly');
      expect(title).toBeInTheDocument();
      expect(title).toHaveClass('text-sm');
    });

    it('should handle large numeric values', () => {
      render(
        <MetricWidget
          title="Revenue"
          value="$1,234,567,890"
          growth={mockGrowthData}
        />
      );

      expect(screen.getByText('$1,234,567,890')).toBeInTheDocument();
    });

    it('should display multiple trend indicators simultaneously', () => {
      const { container } = render(
        <MetricWidget
          title="Revenue"
          value="$1000"
          growth={mockGrowthData}
        />
      );

      // Should have trend indicators for both weekly and monthly
      const trendBadges = container.querySelectorAll('.rounded-full');
      expect(trendBadges.length).toBeGreaterThanOrEqual(2);
    });
  });
});
