import { beforeEach, describe, expect, jest, test } from '@jest/globals';
import { getUserById } from '../src/controllers/users';

const transactionQueryMock = jest.fn();

jest.mock('../src/server-utils/query', () => {
  return {
    default: jest.fn(),
    query: jest.fn(),
    transaction: jest.fn((cb: any) => cb(transactionQueryMock)),
  };
});

describe('getUserById', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('gets basic tier user information by user id', async () => {
    const id = 1234;
    const email = 'keith@wavtool.com';
    const display_name = 'Basic Keith';
    const plan_id = null;
    const plan_name = null;
    const will_renew = null;
    const subscribed_until = null;
    const trial = false;
    const environment = 'production';

    const query = require('../src/server-utils/query').query;
    query
      .mockReturnValueOnce(
        Promise.resolve({
          rows: [
            {
              id,
              email,
              display_name,
              plan_id,
              plan_name,
              will_renew,
              subscribed_until,
              trial,
              environment,
            },
          ],
        })
      )
      .mockReturnValueOnce({ rows: [] }) // plan overrides
      .mockReturnValueOnce({ rows: [] }); // subscription updates

    const user = await getUserById(id);

    expect(user?.email).toBe(email);
    expect(user?.environment).toBe(environment);
    expect(user?.id).toBe(id);
    expect(user?.trial).toBeFalsy();
    expect(user?.trialEligible).toBe(true);
  });

  test('gets indie tier user information by user id', async () => {
    const id = 1234;
    const email = 'keith@wavtool.com';
    const display_name = 'Indie Keith';
    const environment = 'production';
    const paddle_customer_id = 'cus_1234';
    const nextBilledAt = '1720078612661';
    const PADDLE_SUBSCRIPTION_ID = 'sub_1234';
    const INDIE_PRICE_ID = process.env.PADDLE_INDIE_SUBSCRIPTION_PRICE_ID;
    const PLAN_ID = 'INDIE_PLAN_ID';
    const PLAN_NAME = 'Indie';

    const query = require('../src/server-utils/query').query;
    query
      .mockReturnValueOnce(
        Promise.resolve({
          rows: [
            {
              id,
              email,
              paddle_customer_id,
              display_name,
              environment,
              share_name: null,
              user_categorisation: null,
            },
          ],
        })
      )
      .mockReturnValueOnce({ rows: [{ payload: { data: { id: PADDLE_SUBSCRIPTION_ID, status: 'active' } } }] }) // paddle subscription events
      .mockReturnValueOnce({
        rows: [
          {
            payload: {
              data: {
                id: PADDLE_SUBSCRIPTION_ID,
                status: 'active',
                nextBilledAt,
                scheduledChange: null,
                items: [{ price: { id: INDIE_PRICE_ID } }],
              },
            },
          },
        ],
      }) // paddle subscription events
      .mockReturnValueOnce(Promise.resolve({ rows: [{ id: PLAN_ID }] })) // SELECT id FROM plans WHERE paddle_price_id_env_var = $1
      .mockReturnValueOnce({ rows: [{ name: PLAN_NAME, is_trial: false }] }) // SELECT name, is_trial FROM plans WHERE id = $1
      .mockReturnValueOnce({ rows: [] }) // plan overrides
      .mockReturnValueOnce({ rows: [] }) // subscription updates
      .mockReturnValueOnce({ rows: [] }); // getFeaturesForPlan

    const user = await getUserById(id);

    expect(user?.id).toBe(id);
    expect(user?.email).toBe(email);
    expect(user?.username).toBe(display_name);
    expect(user?.shareName).toBeNull();
    expect(user?.willRenew).toBe(true);
    expect(user?.subscribedUntil).toBe(nextBilledAt);
    expect(user?.plan).toBe(PLAN_NAME);
    expect(user?.trialEligible).toBe(false);
    expect(user?.trial).toBeFalsy();
    expect(user?.environment).toBe(environment);
  });

  test('gets pro tier user information by user id', async () => {
    const id = 1234;
    const email = 'keith@wavtool.com';
    const display_name = 'Pro Keith';
    const environment = 'production';
    const paddle_customer_id = 'cus_1234';
    const nextBilledAt = '1720078612661';
    const PADDLE_SUBSCRIPTION_ID = 'sub_1234';
    const PRO_PRICE_ID = process.env.PADDLE_PRO_SUBSCRIPTION_PRICE_ID;
    const PLAN_ID = 'PRO_PLAN_ID';
    const PLAN_NAME = 'Pro';

    const query = require('../src/server-utils/query').query;
    query
      .mockReturnValueOnce(
        Promise.resolve({
          rows: [
            {
              id,
              email,
              paddle_customer_id,
              display_name,
              environment,
              share_name: null,
              user_categorisation: null,
            },
          ],
        })
      )
      .mockReturnValueOnce({ rows: [{ payload: { data: { id: PADDLE_SUBSCRIPTION_ID, status: 'active' } } }] }) // paddle subscription events
      .mockReturnValueOnce({
        rows: [
          {
            payload: {
              data: {
                id: PADDLE_SUBSCRIPTION_ID,
                status: 'active',
                nextBilledAt,
                scheduledChange: null,
                items: [{ price: { id: PRO_PRICE_ID } }],
              },
            },
          },
        ],
      }) // paddle subscription events
      .mockReturnValueOnce(Promise.resolve({ rows: [{ id: PLAN_ID }] })) // SELECT id FROM plans WHERE paddle_price_id_env_var = $1
      .mockReturnValueOnce({ rows: [{ name: PLAN_NAME, is_trial: false }] }) // SELECT name, is_trial FROM plans WHERE id = $1
      .mockReturnValueOnce({ rows: [] }) // plan overrides
      .mockReturnValueOnce({ rows: [] }); // subscription updates

    const user = await getUserById(id);

    expect(user?.id).toBe(id);
    expect(user?.email).toBe(email);
    expect(user?.username).toBe(display_name);
    expect(user?.shareName).toBeNull();
    expect(user?.willRenew).toBe(true);
    expect(user?.subscribedUntil).toBe(nextBilledAt);
    expect(user?.plan).toBe(PLAN_NAME);
    expect(user?.trialEligible).toBe(false);
    expect(user?.trial).toBeFalsy();
    expect(user?.environment).toBe(environment);
  });

  test('gets pro TRIAL tier user information by user id', async () => {
    const id = 1234;
    const email = 'keith@wavtool.com';
    const display_name = 'Pro Trial Keith';
    const environment = 'production';
    const paddle_customer_id = 'cus_1234';
    const nextBilledAt = '1720078612661';
    const PADDLE_SUBSCRIPTION_ID = 'sub_1234';
    const PRO_PRICE_ID = process.env.PADDLE_PRO_SUBSCRIPTION_PRICE_ID;
    const PLAN_ID = 'PRO_PLAN_ID';
    const PLAN_NAME = 'Pro';

    const query = require('../src/server-utils/query').query;
    query
      .mockReturnValueOnce(
        Promise.resolve({
          rows: [
            {
              id,
              email,
              paddle_customer_id,
              display_name,
              environment,
              share_name: null,
              user_categorisation: null,
            },
          ],
        })
      )
      .mockReturnValueOnce({ rows: [{ payload: { data: { id: PADDLE_SUBSCRIPTION_ID, status: 'trialing' } } }] }) // paddle subscription events
      .mockReturnValueOnce({
        rows: [
          {
            payload: {
              data: {
                id: PADDLE_SUBSCRIPTION_ID,
                status: 'trialing',
                nextBilledAt,
                scheduledChange: null,
                items: [{ price: { id: PRO_PRICE_ID } }],
              },
            },
          },
        ],
      }) // paddle subscription events
      .mockReturnValueOnce(Promise.resolve({ rows: [{ id: PLAN_ID }] })) // SELECT id FROM plans WHERE paddle_price_id_env_var = $1
      .mockReturnValueOnce({ rows: [{ name: PLAN_NAME, is_trial: true }] }) // SELECT name, is_trial FROM plans WHERE id = $1
      .mockReturnValueOnce({ rows: [] }) // plan overrides
      .mockReturnValueOnce({ rows: [] }); // get features for plan

    const user = await getUserById(id);

    expect(user?.id).toBe(id);
    expect(user?.email).toBe(email);
    expect(user?.username).toBe(display_name);
    expect(user?.shareName).toBeNull();
    expect(user?.willRenew).toBe(true);
    expect(user?.subscribedUntil).toBe(nextBilledAt);
    expect(user?.plan).toBe(PLAN_NAME);
    expect(user?.trialEligible).toBe(false);
    expect(user?.trial).toBe(true);
    expect(user?.environment).toBe(environment);
  });

  test('gets pro tier user information by user id (upgraded indie to pro)', async () => {
    const id = 1234;
    const email = 'keith@wavtool.com';
    const display_name = 'Pro Trial Keith';
    const environment = 'production';
    const paddle_customer_id = 'cus_1234';
    const nextBilledAt = '1720078612661';
    const PREV_PADDLE_SUBSCRIPTION_ID = 'trial_sub_1234';
    const PADDLE_SUBSCRIPTION_ID = 'sub_1234';
    const PRO_PRICE_ID = process.env.PADDLE_PRO_SUBSCRIPTION_PRICE_ID;
    const PLAN_ID = 'PRO_PLAN_ID';
    const PLAN_NAME = 'Pro';

    const query = require('../src/server-utils/query').query;
    query
      .mockReturnValueOnce(
        // get user object
        Promise.resolve({
          rows: [
            {
              id,
              email,
              paddle_customer_id,
              display_name,
              environment,
              share_name: null,
              user_categorisation: null,
            },
          ],
        })
      )
      .mockReturnValueOnce({
        // get paddle subscription events
        rows: [
          { payload: { data: { id: PREV_PADDLE_SUBSCRIPTION_ID, status: 'canceled' } } },
          { payload: { data: { id: PADDLE_SUBSCRIPTION_ID, status: 'active' } } },
        ],
      })
      .mockReturnValueOnce({
        // get paddle subscription object
        rows: [
          {
            payload: {
              data: {
                id: PADDLE_SUBSCRIPTION_ID,
                status: 'active',
                nextBilledAt,
                scheduledChange: null,
                items: [{ price: { id: PRO_PRICE_ID } }],
              },
            },
          },
        ],
      })
      .mockReturnValueOnce(Promise.resolve({ rows: [{ id: PLAN_ID }] })) // SELECT id FROM plans WHERE paddle_price_id_env_var = $1
      .mockReturnValueOnce({ rows: [{ name: PLAN_NAME, is_trial: false }] }) // SELECT name, is_trial FROM plans WHERE id = $1
      .mockReturnValueOnce({ rows: [] }) // get plan overrides
      .mockReturnValueOnce({ rows: [] }); // get features for plan

    const user = await getUserById(id);

    expect(query.mock.calls[2][1]).toEqual([PADDLE_SUBSCRIPTION_ID]);
    expect(user?.id).toBe(id);
    expect(user?.email).toBe(email);
    expect(user?.username).toBe(display_name);
    expect(user?.shareName).toBeNull();
    expect(user?.willRenew).toBe(true);
    expect(user?.subscribedUntil).toBe(nextBilledAt);
    expect(user?.plan).toBe(PLAN_NAME);
    expect(user?.trialEligible).toBe(false);
    expect(user?.trial).toBeFalsy();
    expect(user?.environment).toBe(environment);
  });
});
