import { Op } from 'sequelize';

import { getCreditGrantStats } from '../../src/libs/credit-grant';
import { CreditGrant, PaymentCurrency } from '../../src/store/models';

jest.mock('../../src/libs/logger', () => ({
  __esModule: true,
  default: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    debug: jest.fn(),
  },
}));

jest.mock('../../src/libs/subscription', () => ({
  getMeterPriceIdsFromSubscription: jest.fn(),
}));

jest.mock('../../src/store/models', () => ({
  CreditGrant: {
    findAll: jest.fn(),
  },
  PaymentCurrency: {
    findByPk: jest.fn(),
  },
  Customer: {
    findByPk: jest.fn(),
  },
  Subscription: {
    findByPk: jest.fn(),
  },
}));

describe('libs/credit-grant.ts', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    jest.restoreAllMocks();
  });

  it('aggregates daily stats and totals', async () => {
    const currencyJson = { id: 'cur_1', name: 'USD', symbol: '$', decimal: 2 };
    (PaymentCurrency.findByPk as jest.Mock).mockResolvedValue({
      ...currencyJson,
      toJSON: () => currencyJson,
    });

    (CreditGrant.findAll as jest.Mock).mockResolvedValue([
      {
        amount: '100',
        remaining_amount: '40',
        created_at: new Date('2024-01-01T01:00:00Z'),
      },
      {
        amount: '200',
        remaining_amount: '200',
        created_at: new Date('2024-01-01T10:00:00Z'),
      },
      {
        amount: '50',
        remaining_amount: '10',
        created_at: new Date('2024-01-02T05:00:00Z'),
      },
    ]);

    const result = await getCreditGrantStats({
      currencyId: 'cur_1',
      startDate: 1704067200,
      endDate: 1704240000,
    });

    expect(PaymentCurrency.findByPk).toHaveBeenCalledWith('cur_1', {
      attributes: ['id', 'name', 'symbol', 'decimal'],
    });

    const callArg = (CreditGrant.findAll as jest.Mock).mock.calls[0][0];
    expect(callArg.attributes).toEqual(['amount', 'remaining_amount', 'created_at']);
    expect(callArg.raw).toBe(true);

    expect(result.stats).toEqual({
      currency_id: 'cur_1',
      currency: currencyJson,
      grant_count: 3,
      total_granted: '350',
      total_remaining: '250',
      total_consumed: '100',
    });

    expect(result.daily_stats).toEqual([
      {
        date: '2024-01-01',
        currency_id: 'cur_1',
        grant_count: 2,
        total_granted: '300',
        total_remaining: '240',
        total_consumed: '60',
      },
      {
        date: '2024-01-02',
        currency_id: 'cur_1',
        grant_count: 1,
        total_granted: '50',
        total_remaining: '10',
        total_consumed: '40',
      },
    ]);
  });

  it('groups grants by day using timezone offset', async () => {
    const currencyJson = { id: 'cur_2', name: 'USD', symbol: '$', decimal: 2 };
    (PaymentCurrency.findByPk as jest.Mock).mockResolvedValue({
      ...currencyJson,
      toJSON: () => currencyJson,
    });

    (CreditGrant.findAll as jest.Mock).mockResolvedValue([
      {
        amount: '10',
        remaining_amount: '5',
        created_at: new Date('2024-01-01T23:30:00Z'),
      },
      {
        amount: '20',
        remaining_amount: '0',
        created_at: new Date('2024-01-02T01:00:00Z'),
      },
    ]);

    const result = await getCreditGrantStats({
      currencyId: 'cur_2',
      startDate: 1704067200,
      endDate: 1704240000,
      timezoneOffset: 480,
    });

    expect(result.daily_stats).toEqual([
      {
        date: '2024-01-02',
        currency_id: 'cur_2',
        grant_count: 2,
        total_granted: '30',
        total_remaining: '5',
        total_consumed: '25',
      },
    ]);
  });

  it('builds where clause with grantedBy and category filters', async () => {
    const currencyJson = { id: 'cur_3', name: 'USD', symbol: '$', decimal: 2 };
    (PaymentCurrency.findByPk as jest.Mock).mockResolvedValue({
      ...currencyJson,
      toJSON: () => currencyJson,
    });
    (CreditGrant.findAll as jest.Mock).mockResolvedValue([]);

    const startDate = 1704067200;
    const endDate = 1704153600;

    const result = await getCreditGrantStats({
      currencyId: 'cur_3',
      startDate,
      endDate,
      grantedBy: 'did:example:123',
      category: 'paid',
    });

    const callArg = (CreditGrant.findAll as jest.Mock).mock.calls[0][0];
    expect(callArg.where.currency_id).toBe('cur_3');
    expect(callArg.where['metadata.granted_by']).toBe('did:example:123');
    expect(callArg.where.category).toBe('paid');
    expect(callArg.where.created_at[Op.gte]).toEqual(new Date(startDate * 1000));
    expect(callArg.where.created_at[Op.lte]).toEqual(new Date(endDate * 1000));

    expect(result.stats).toEqual({
      currency_id: 'cur_3',
      currency: currencyJson,
      grant_count: 0,
      total_granted: '0',
      total_remaining: '0',
      total_consumed: '0',
    });
    expect(result.daily_stats).toEqual([]);
  });
});
