import { describe, expect, it } from 'vitest';
import {
  convertToVideoCoordinates,
  mergeDetectedShapesIntoTrackedCards,
  rectanglesOverlap,
  selectCardsReadyForVerification,
  TRACK_STALE_MS,
} from '../../lib/scanner-card-detection.js';

describe('rectanglesOverlap', () => {
  it('returns true when rectangles mostly overlap', () => {
    const a = { x: 0, y: 0, width: 100, height: 140 };
    const b = { x: 10, y: 10, width: 100, height: 140 };
    expect(rectanglesOverlap(a, b, 0.3)).toBe(true);
  });

  it('returns false when rectangles do not touch', () => {
    const a = { x: 0, y: 0, width: 50, height: 70 };
    const b = { x: 200, y: 200, width: 50, height: 70 };
    expect(rectanglesOverlap(a, b)).toBe(false);
  });
});

describe('convertToVideoCoordinates', () => {
  it('scales detection canvas coords to video pixel space', () => {
    const canvas = { width: 320, height: 240 };
    const video = { videoWidth: 1280, videoHeight: 960 };
    expect(convertToVideoCoordinates({ x: 10, y: 20, width: 100, height: 140 }, canvas, video)).toEqual({
      x: 40,
      y: 80,
      width: 400,
      height: 560,
    });
  });
});

describe('mergeDetectedShapesIntoTrackedCards', () => {
  it('creates a new tracked card for an unmatched shape', () => {
    const now = 1_000_000;
    const shape = { x: 10, y: 20, width: 100, height: 140, score: 80 };

    const { cards, nextCardId } = mergeDetectedShapesIntoTrackedCards([], [shape], 1, now);

    expect(cards).toHaveLength(1);
    expect(cards[0]).toMatchObject({
      id: 1,
      status: 'detecting',
      stableCount: 1,
      scanAttempts: 0,
      bounds: shape,
    });
    expect(nextCardId).toBe(2);
  });

  it('updates an overlapping tracked card instead of creating a duplicate', () => {
    const now = 1_000_000;
    const existing = {
      id: 5,
      bounds: { x: 10, y: 20, width: 100, height: 140 },
      status: 'detecting',
      firstSeen: now - 500,
      lastSeen: now - 100,
      stableCount: 3,
      scanAttempts: 0,
    };
    const moved = { x: 12, y: 22, width: 100, height: 140, score: 82 };

    const { cards, nextCardId } = mergeDetectedShapesIntoTrackedCards([existing], [moved], 6, now);

    expect(cards).toHaveLength(1);
    expect(cards[0].id).toBe(5);
    expect(cards[0].stableCount).toBe(4);
    expect(cards[0].lastSeen).toBe(now);
    expect(nextCardId).toBe(6);
  });

  it('drops cards not seen within TRACK_STALE_MS', () => {
    const now = 10_000;
    const stale = {
      id: 1,
      bounds: { x: 0, y: 0, width: 50, height: 70 },
      status: 'detecting',
      firstSeen: now - TRACK_STALE_MS - 1,
      lastSeen: now - TRACK_STALE_MS - 1,
      stableCount: 2,
      scanAttempts: 0,
    };

    const { cards } = mergeDetectedShapesIntoTrackedCards([stale], [], 2, now);
    expect(cards).toHaveLength(0);
  });
});

describe('selectCardsReadyForVerification', () => {
  it('returns detecting cards that are stable long enough', () => {
    const now = 10_000;
    const ready = {
      id: 1,
      status: 'detecting',
      stableCount: 6,
      scanAttempts: 0,
      firstSeen: now - 3000,
    };
    const tooFresh = {
      id: 2,
      status: 'detecting',
      stableCount: 6,
      scanAttempts: 0,
      firstSeen: now - 1000,
    };

    expect(selectCardsReadyForVerification([ready, tooFresh], now)).toEqual([ready]);
  });

  it('excludes cards that already attempted verification', () => {
    const now = 10_000;
    const retried = {
      id: 1,
      status: 'detecting',
      stableCount: 8,
      scanAttempts: 1,
      firstSeen: now - 5000,
    };

    expect(selectCardsReadyForVerification([retried], now)).toHaveLength(0);
  });
});
