import { OperateGesture, IPointXY } from '../gesture';

describe('OperateGesture', () => {
  let mockElement: any;
  let options: any;

  beforeEach(() => {
    // Create a mock element with DOMRect-like structure
    mockElement = {
      addEventListener: jest.fn(),
      removeEventListener: jest.fn(),
      setPointerCapture: jest.fn(),
      releasePointerCapture: jest.fn(),
    };

    options = {
      tap: jest.fn(),
      singleTap: jest.fn(),
      doubleTap: jest.fn(),
      longTap: jest.fn(),
      drag: jest.fn(),
      pointerdown: jest.fn(),
      pointermove: jest.fn(),
      pointerup: jest.fn(),
      pointercancel: jest.fn(),
      wheel: jest.fn(),
    };
  });

  describe('constructor', () => {
    it('should initialize with element and options', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      expect(gesture.element).toBe(mockElement);
      expect(gesture.options).toBe(options);
      expect(mockElement.addEventListener).toHaveBeenCalled();
    });

    it('should bind event listeners', () => {
      new OperateGesture(mockElement as any, options);
      expect(mockElement.addEventListener).toHaveBeenCalledWith('pointerdown', expect.any(Function));
      expect(mockElement.addEventListener).toHaveBeenCalledWith('pointermove', expect.any(Function));
      expect(mockElement.addEventListener).toHaveBeenCalledWith('pointerup', expect.any(Function));
      expect(mockElement.addEventListener).toHaveBeenCalledWith('pointercancel', expect.any(Function));
      expect(mockElement.addEventListener).toHaveBeenCalledWith('wheel', expect.any(Function));
    });
  });

  describe('getDistance', () => {
    it('should calculate distance between two points', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const point1: IPointXY = { x: 0, y: 0 };
      const point2: IPointXY = { x: 3, y: 4 };
      const distance = gesture.getDistance(point1, point2);
      expect(distance).toBe(5); // 3-4-5 triangle
    });

    it('should return 0 for same points', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const point: IPointXY = { x: 5, y: 5 };
      const distance = gesture.getDistance(point, point);
      expect(distance).toBe(0);
    });
  });

  describe('getCenter', () => {
    it('should calculate center point between two points', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const point1: IPointXY = { x: 0, y: 0 };
      const point2: IPointXY = { x: 10, y: 20 };
      const center = gesture.getCenter(point1, point2);
      expect(center).toEqual({ x: 5, y: 10 });
    });
  });

  describe('getDragDirection', () => {
    it('should return "right" for horizontal drag to the right', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      gesture.distance = { x: 20, y: 5 };
      const direction = gesture.getDragDirection();
      expect(direction).toBe('right');
    });

    it('should return "left" for horizontal drag to the left', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      gesture.distance = { x: -20, y: 5 };
      const direction = gesture.getDragDirection();
      expect(direction).toBe('left');
    });

    it('should return "down" for vertical drag downward', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      gesture.distance = { x: 5, y: 20 };
      const direction = gesture.getDragDirection();
      expect(direction).toBe('down');
    });

    it('should return "up" for vertical drag upward', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      gesture.distance = { x: 5, y: -20 };
      const direction = gesture.getDragDirection();
      expect(direction).toBe('up');
    });
  });

  describe('getImgSize', () => {
    it('should return original size if image fits within max dimensions', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const size = gesture.getImgSize(100, 100, 200, 200);
      expect(size).toEqual({ width: 100, height: 100 });
    });

    it('should scale down width if image is wider than max width', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const size = gesture.getImgSize(400, 200, 200, 200);
      expect(size.width).toBe(200);
      expect(size.height).toBe(100);
    });

    it('should scale down height if image is taller than max height', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const size = gesture.getImgSize(200, 400, 200, 200);
      expect(size.width).toBe(100);
      expect(size.height).toBe(200);
    });
  });

  describe('handleWheel', () => {
    it('should set in_scale to 1.05 for upward scroll', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const event: any = { deltaY: -10 };
      gesture.handleWheel(event);
      expect(event.in_scale).toBe(1.05);
      expect(options.wheel).toHaveBeenCalledWith(event);
    });

    it('should set in_scale to 1/1.05 for downward scroll', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      const event: any = { deltaY: 10 };
      gesture.handleWheel(event);
      expect(event.in_scale).toBe(1 / 1.05);
      expect(options.wheel).toHaveBeenCalledWith(event);
    });
  });

  describe('destroy', () => {
    it('should remove all event listeners', () => {
      const gesture = new OperateGesture(mockElement as any, options);
      gesture.destroy();
      expect(mockElement.removeEventListener).toHaveBeenCalledWith('pointerdown', expect.any(Function));
      expect(mockElement.removeEventListener).toHaveBeenCalledWith('pointermove', expect.any(Function));
      expect(mockElement.removeEventListener).toHaveBeenCalledWith('pointerup', expect.any(Function));
      expect(mockElement.removeEventListener).toHaveBeenCalledWith('pointercancel', expect.any(Function));
      expect(mockElement.removeEventListener).toHaveBeenCalledWith('wheel', expect.any(Function));
    });
  });
});

