/**
 * Tests for POSContext operations
 *
 * Tests cover cart operations including adding items, removing items,
 * updating quantities, applying discounts, and calculating totals.
 */

import React, { type ReactNode } from 'react';
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { POSProvider, usePOS } from '../context/POSContext';
import type { POSProduct, POSService, POSDiscount } from '../types';

// Clear localStorage before each test to prevent state leakage
beforeEach(() => {
  localStorage.clear();
});

// Create wrapper component for hooks
const createWrapper = (initialLocationId?: number) => {
  return ({ children }: { children: ReactNode }) => (
    <POSProvider initialLocationId={initialLocationId ?? null}>{children}</POSProvider>
  );
};

// Mock product - matches POSProduct interface from types.ts
const mockProduct: POSProduct = {
  id: 1,
  name: 'Test Product',
  sku: 'TEST-001',
  barcode: '123456789',
  description: 'A test product',
  price_cents: 1000,
  cost_cents: 500,
  tax_rate: 0.08,
  is_taxable: true,
  category_id: 1,
  display_order: 1,
  image_url: null,
  color: '#3B82F6',
  status: 'active',
  track_inventory: true,
  created_at: '2024-01-01T00:00:00Z',
  updated_at: '2024-01-01T00:00:00Z',
};

// Mock service - matches POSService interface from types.ts
const mockService: POSService = {
  id: 2,
  name: 'Test Service',
  description: 'A test service',
  price_cents: 2500,
  duration_minutes: 60,
};

describe('POSContext - Initial State', () => {
  it('should start with empty cart', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    expect(result.current.state.cart.items).toHaveLength(0);
    expect(result.current.isCartEmpty).toBe(true);
    expect(result.current.itemCount).toBe(0);
  });

  it('should start with zero totals', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    expect(result.current.state.cart.subtotalCents).toBe(0);
    expect(result.current.state.cart.taxCents).toBe(0);
    expect(result.current.state.cart.tipCents).toBe(0);
    expect(result.current.state.cart.discountCents).toBe(0);
    expect(result.current.state.cart.totalCents).toBe(0);
  });

  it('should accept initial location ID', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(42),
    });

    expect(result.current.state.selectedLocationId).toBe(42);
  });

  it('should start with no customer', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    expect(result.current.state.cart.customer).toBeNull();
  });

  it('should start with no active shift', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    expect(result.current.state.activeShift).toBeNull();
  });

  it('should start with printer disconnected', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    expect(result.current.state.printerStatus).toBe('disconnected');
  });
});

describe('POSContext - Adding Items', () => {
  it('should add a product to cart', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    expect(result.current.state.cart.items).toHaveLength(1);
    expect(result.current.state.cart.items[0].name).toBe('Test Product');
    expect(result.current.state.cart.items[0].unitPriceCents).toBe(1000);
    expect(result.current.state.cart.items[0].quantity).toBe(1);
  });

  it('should add a service to cart', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockService, 1, 'service');
    });

    expect(result.current.state.cart.items).toHaveLength(1);
    expect(result.current.state.cart.items[0].name).toBe('Test Service');
    expect(result.current.state.cart.items[0].unitPriceCents).toBe(2500);
    expect(result.current.state.cart.items[0].itemType).toBe('service');
  });

  it('should increment quantity for existing item', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    // Should still be one item with quantity 2
    expect(result.current.state.cart.items).toHaveLength(1);
    expect(result.current.state.cart.items[0].quantity).toBe(2);
  });

  it('should add multiple quantities at once', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 5, 'product');
    });

    expect(result.current.state.cart.items[0].quantity).toBe(5);
    expect(result.current.itemCount).toBe(5);
  });

  it('should update item count correctly', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product');
      result.current.addItem(mockService, 1, 'service');
    });

    expect(result.current.itemCount).toBe(3);
    expect(result.current.isCartEmpty).toBe(false);
  });
});

describe('POSContext - Removing Items', () => {
  it('should remove item from cart', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    const itemId = result.current.state.cart.items[0].id;

    act(() => {
      result.current.removeItem(itemId);
    });

    expect(result.current.state.cart.items).toHaveLength(0);
    expect(result.current.isCartEmpty).toBe(true);
  });

  it('should handle removing non-existent item gracefully', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    act(() => {
      result.current.removeItem('non-existent-id');
    });

    // Should still have the original item
    expect(result.current.state.cart.items).toHaveLength(1);
  });

  it('should recalculate totals after removal', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
      result.current.addItem(mockService, 1, 'service');
    });

    const productItemId = result.current.state.cart.items[0].id;

    act(() => {
      result.current.removeItem(productItemId);
    });

    // Should only have service now
    expect(result.current.state.cart.subtotalCents).toBe(2500);
  });
});

describe('POSContext - Updating Quantities', () => {
  it('should update item quantity', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    const itemId = result.current.state.cart.items[0].id;

    act(() => {
      result.current.updateQuantity(itemId, 5);
    });

    expect(result.current.state.cart.items[0].quantity).toBe(5);
    expect(result.current.itemCount).toBe(5);
  });

  it('should remove item when quantity set to zero', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 3, 'product');
    });

    const itemId = result.current.state.cart.items[0].id;

    act(() => {
      result.current.updateQuantity(itemId, 0);
    });

    expect(result.current.state.cart.items).toHaveLength(0);
  });

  it('should remove item when quantity is negative', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product');
    });

    const itemId = result.current.state.cart.items[0].id;

    act(() => {
      result.current.updateQuantity(itemId, -1);
    });

    expect(result.current.state.cart.items).toHaveLength(0);
  });

  it('should recalculate totals after quantity update', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    const initialSubtotal = result.current.state.cart.subtotalCents;
    const itemId = result.current.state.cart.items[0].id;

    act(() => {
      result.current.updateQuantity(itemId, 3);
    });

    expect(result.current.state.cart.subtotalCents).toBe(initialSubtotal * 3);
  });
});

describe('POSContext - Applying Discounts', () => {
  it('should apply percentage discount to cart', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product');
    });

    const discount: POSDiscount = {
      percent: 10,
      reason: 'Loyalty discount',
    };

    act(() => {
      result.current.applyDiscount(discount);
    });

    expect(result.current.state.cart.discount).toEqual(discount);
    // 10% of $20.00 = $2.00 = 200 cents
    expect(result.current.state.cart.discountCents).toBe(200);
  });

  it('should apply fixed amount discount to cart', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product');
    });

    const discount: POSDiscount = {
      amountCents: 500,
      reason: 'Promo code',
    };

    act(() => {
      result.current.applyDiscount(discount);
    });

    expect(result.current.state.cart.discountCents).toBe(500);
  });

  it('should clear discount', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product');
      result.current.applyDiscount({ percent: 10 });
    });

    act(() => {
      result.current.clearDiscount();
    });

    expect(result.current.state.cart.discount).toBeNull();
    expect(result.current.state.cart.discountCents).toBe(0);
  });

  it('should apply item-level discount', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    const itemId = result.current.state.cart.items[0].id;

    act(() => {
      result.current.setItemDiscount(itemId, undefined, 20); // 20% off
    });

    expect(result.current.state.cart.items[0].discountPercent).toBe(20);
    // Subtotal should reflect the discount
    expect(result.current.state.cart.subtotalCents).toBe(800); // $10 - 20% = $8
  });
});

describe('POSContext - Setting Tip', () => {
  it('should set tip amount', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    act(() => {
      result.current.setTip(200);
    });

    expect(result.current.state.cart.tipCents).toBe(200);
  });

  it('should include tip in total', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
    });

    const totalBeforeTip = result.current.state.cart.totalCents;

    act(() => {
      result.current.setTip(300);
    });

    expect(result.current.state.cart.totalCents).toBe(totalBeforeTip + 300);
  });

  it('should handle zero tip', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product');
      result.current.setTip(500);
    });

    act(() => {
      result.current.setTip(0);
    });

    expect(result.current.state.cart.tipCents).toBe(0);
  });
});

describe('POSContext - Calculating Totals', () => {
  it('should calculate subtotal correctly', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product'); // $10 x 2 = $20
      result.current.addItem(mockService, 1, 'service'); // $25
    });

    expect(result.current.state.cart.subtotalCents).toBe(4500); // $45
  });

  it('should calculate tax correctly', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product'); // $10 @ 8% = $0.80 tax
    });

    expect(result.current.state.cart.taxCents).toBe(80);
  });

  it('should calculate total correctly', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product'); // $10 + $0.80 tax
    });

    expect(result.current.state.cart.totalCents).toBe(1080);
  });

  it('should calculate total with discount and tip', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product'); // $20 subtotal + $1.60 tax
      result.current.applyDiscount({ amountCents: 500 }); // -$5 discount
      result.current.setTip(300); // +$3 tip
    });

    // Total: $20 + $1.60 - $5 + $3 = $19.60
    expect(result.current.state.cart.totalCents).toBe(1960);
  });

  it('should not allow negative total', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 1, 'product'); // $10.80 total
      result.current.applyDiscount({ amountCents: 2000 }); // $20 discount (exceeds total)
    });

    expect(result.current.state.cart.totalCents).toBeGreaterThanOrEqual(0);
  });
});

describe('POSContext - Customer Management', () => {
  it('should set customer', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    const customer = {
      id: 1,
      name: 'John Doe',
      email: 'john@example.com',
      phone: '555-123-4567',
    };

    act(() => {
      result.current.setCustomer(customer);
    });

    expect(result.current.state.cart.customer).toEqual(customer);
  });

  it('should clear customer', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.setCustomer({ id: 1, name: 'John', email: '', phone: '' });
    });

    act(() => {
      result.current.setCustomer(null);
    });

    expect(result.current.state.cart.customer).toBeNull();
  });
});

describe('POSContext - Clearing Cart', () => {
  it('should clear all items from cart', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.addItem(mockProduct, 2, 'product');
      result.current.addItem(mockService, 1, 'service');
      result.current.setTip(500);
      result.current.applyDiscount({ percent: 10 });
    });

    act(() => {
      result.current.clearCart();
    });

    expect(result.current.state.cart.items).toHaveLength(0);
    expect(result.current.state.cart.subtotalCents).toBe(0);
    expect(result.current.state.cart.taxCents).toBe(0);
    expect(result.current.state.cart.tipCents).toBe(0);
    expect(result.current.state.cart.discountCents).toBe(0);
    expect(result.current.state.cart.totalCents).toBe(0);
    expect(result.current.state.cart.discount).toBeNull();
    expect(result.current.state.cart.customer).toBeNull();
  });
});

describe('POSContext - Active Shift', () => {
  it('should set active shift', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    const shift = {
      id: 1,
      location: 1,
      opened_by: 1,
      closed_by: null,
      opening_balance_cents: 10000,
      expected_balance_cents: 15000,
      actual_balance_cents: null,
      variance_cents: null,
      cash_breakdown: {},
      status: 'open' as const,
      opened_at: '2024-01-01T09:00:00Z',
      closed_at: null,
      opening_notes: '',
      closing_notes: '',
    };

    act(() => {
      result.current.setActiveShift(shift);
    });

    expect(result.current.state.activeShift).toEqual(shift);
  });

  it('should clear active shift', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.setActiveShift({ id: 1 } as any);
    });

    act(() => {
      result.current.setActiveShift(null);
    });

    expect(result.current.state.activeShift).toBeNull();
  });
});

describe('POSContext - Printer Status', () => {
  it('should update printer status', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.setPrinterStatus('connecting');
    });

    expect(result.current.state.printerStatus).toBe('connecting');

    act(() => {
      result.current.setPrinterStatus('connected');
    });

    expect(result.current.state.printerStatus).toBe('connected');
  });
});

describe('POSContext - Location', () => {
  it('should set location', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(),
    });

    act(() => {
      result.current.setLocation(123);
    });

    expect(result.current.state.selectedLocationId).toBe(123);
  });

  it('should clear location', () => {
    const { result } = renderHook(() => usePOS(), {
      wrapper: createWrapper(42),
    });

    act(() => {
      result.current.setLocation(null);
    });

    expect(result.current.state.selectedLocationId).toBeNull();
  });
});

describe('POSContext - usePOS Hook Error', () => {
  it('should throw error when used outside provider', () => {
    // Suppress console.error for this test
    const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});

    expect(() => {
      renderHook(() => usePOS());
    }).toThrow('usePOS must be used within a POSProvider');

    consoleSpy.mockRestore();
  });
});
