import { describe, expect, it } from 'vitest';
import { AppError } from '../../../shared/errors.js';
import {
  validatePaymentAllocations,
  type ConfiguredPaymentMethod,
} from '../application/create-pos-sale.js';

const methods: ConfiguredPaymentMethod[] = [
  { id: 'cash-id', code: 'cash', label: 'Efectivo', kind: 'cash' },
  { id: 'card-id', code: 'card', label: 'Tarjeta', kind: 'card' },
  { id: 'bizum-id', code: 'bizum', label: 'Bizum', kind: 'other' },
];

describe('POS payment allocation', () => {
  it('supports mixed partial payments and calculates cash change', () => {
    const result = validatePaymentAllocations(
      2_000,
      [
        { methodCode: 'cash', amountCents: 500, tenderedCents: 1_000 },
        { methodCode: 'bizum', amountCents: 1_500 },
      ],
      methods,
    );

    expect(result).toMatchObject([
      { methodCode: 'cash', amountCents: 500, tenderedCents: 1_000, changeCents: 500 },
      { methodCode: 'bizum', amountCents: 1_500, tenderedCents: null, changeCents: 0 },
    ]);
  });

  it('accepts under-allocated amounts (orders may remain PENDING)', () => {
    expect(
      validatePaymentAllocations(2_000, [{ methodCode: 'card', amountCents: 1_999 }], methods),
    ).toEqual([
      {
        methodId: 'card-id',
        methodCode: 'card',
        methodLabel: 'Tarjeta',
        kind: 'card',
        amountCents: 1999,
        tenderedCents: null,
        changeCents: 0,
      },
    ]);
  });

  it('rejects overpayments while still under total', () => {
    expectCode(
      () =>
        validatePaymentAllocations(2_000, [{ methodCode: 'card', amountCents: 2_001 }], methods),
      'POS_PAYMENT_OVERPAYMENT',
    );
  });

  it('rejects disabled methods and tendered amounts on non-cash methods', () => {
    expectCode(
      () => validatePaymentAllocations(100, [{ methodCode: 'stripe', amountCents: 100 }], methods),
      'POS_PAYMENT_METHOD_DISABLED',
    );
    expectCode(
      () =>
        validatePaymentAllocations(
          100,
          [{ methodCode: 'card', amountCents: 100, tenderedCents: 100 }],
          methods,
        ),
      'POS_NON_CASH_TENDERED',
    );
  });

  it('keeps legacy kind mapping for cash and card clients', () => {
    expect(
      validatePaymentAllocations(100, [{ kind: 'card', amountCents: 100 }], methods)[0],
    ).toMatchObject({ methodCode: 'card', kind: 'card' });
  });
});

function expectCode(action: () => unknown, code: string): void {
  try {
    action();
    throw new Error('Expected action to throw');
  } catch (error) {
    expect(error).toBeInstanceOf(AppError);
    expect((error as AppError).code).toBe(code);
  }
}
