// Copyright 2025 Northern.tech AS
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
import { defaultState, render } from '@/testUtils';
import { TIMEOUTS } from '@northern.tech/store/constants';
import * as StoreThunks from '@northern.tech/store/thunks';
import { undefineds } from '@northern.tech/testing/mockData';
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import { act, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';

import { SubscriptionDrawer } from './SubscriptionDrawer';
import { microDeviceTier, standardDeviceTier } from './SubscriptionPage.test';

const createBillingProfileReq = {
  billingProfile: {
    address: {
      city: 'Oslo',
      country: 'PL',
      line1: 'Blindernveien',
      postal_code: '5678'
    },
    email: 'a@b.com',
    name: 'test',
    shipping: {
      name: 'test',
      address: {
        city: 'Oslo',
        country: 'PL',
        line1: 'Blindernveien',
        postal_code: '5678'
      }
    }
  }
};
const preloadedState = {
  organization: {
    ...defaultState.organization,
    organization: {
      ...defaultState.organization.organization,
      billing_profile: {
        email: 'ok@ok.ok',
        name: 'Test account',
        address: {
          country: 'NO',
          city: 'test City',
          line1: 'Test address',
          postal_code: '1234'
        },
        shipping: {
          name: 'Test account',
          address: {
            country: 'NO',
            city: 'test City',
            line1: 'Test address',
            postal_code: '1234'
          }
        }
      },
      subscription: {
        id: 'sub_1RqbZwFlFfXikjZVg0q5xIV1',
        period_start: '2025-07-30T15:06:04Z',
        period_end: '2025-08-30T15:06:04Z',
        total: 3200,
        currency: 'usd',
        lines: [
          {
            description: '50 device × Mender Basic (Tier 1 at $0.00 / month)',
            amount: 0,
            currency: 'usd',
            quantity: 50,
            price_id: 'price_1PYktlFlFfXikjZVh2aNHp7i'
          },
          {
            description: 'Mender Basic (Tier 1 at $32.00 / month)',
            amount: 3200,
            currency: 'usd',
            quantity: 0,
            price_id: 'price_1PYktlFlFfXikjZVh2aNHp7i'
          }
        ],
        status: 'active',
        plan: 'os',
        products: [
          {
            name: 'mender_standard',
            quantity: 50
          }
        ]
      }
    },
    card: {
      last4: '1234',
      expiration: { month: 8, year: 1230 },
      brand: 'Visa'
    }
  }
};
const { plans: PLANS } = defaultState.organization.products;

describe('Subscription Drawer component', () => {
  it('renders correctly', async () => {
    const stripe = loadStripe();

    const { baseElement } = render(
      <Elements stripe={stripe}>
        <SubscriptionDrawer
          deviceTypes={{ ...standardDeviceTier, ...microDeviceTier }}
          organization={defaultState.organization.organization}
          onClose={vi.fn()}
          plan={PLANS.os}
          addons={[]}
          isTrial={true}
          currentPlan={PLANS.os}
        />
      </Elements>
    );
    const view = baseElement.lastElementChild;
    expect(view).toMatchSnapshot();
    expect(view).toEqual(expect.not.stringMatching(undefineds));
  });

  it('allows creating billing profile', { timeout: TIMEOUTS.refreshDefault }, async () => {
    const stripe = loadStripe();

    const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
    const { createBillingProfile } = StoreThunks;
    const ui = (
      <Elements stripe={stripe}>
        <SubscriptionDrawer
          deviceTypes={{ ...standardDeviceTier, ...microDeviceTier }}
          organization={defaultState.organization.organization}
          onClose={vi.fn()}
          plan={PLANS.os}
          addons={[]}
          isTrial={true}
          currentPlan={PLANS.os}
        />
      </Elements>
    );
    render(ui, {
      preloadedState: {
        ...defaultState,
        organization: { ...defaultState.organization, organization: { ...defaultState.organization.organization, trial: true } }
      }
    });
    await act(async () => vi.runOnlyPendingTimers());
    expect(screen.getByText(/Subscribe to Mender Basic/i)).toBeVisible();

    const input = screen.getByLabelText<HTMLInputElement>('Country or region');

    const addressInput = screen.getByRole('textbox', { name: /address line 1/i });
    const cityInput = screen.getByRole('textbox', { name: /city/i });
    const zipInput = screen.getByRole('textbox', { name: /zip or postal code/i });

    await user.type(addressInput, 'Blindernveien');
    await user.type(cityInput, 'Oslo');
    await act(async () => await user.type(zipInput, '5678'));

    const countryAutoComplete = screen.getByRole('combobox', { name: /country/i });
    await act(async () => {
      await user.type(countryAutoComplete, 'Polan');
      await user.keyboard('[ArrowUp]');
      await user.keyboard('[Enter]');
    });

    expect(input.value).toEqual('Poland');

    await act(async () => await user.click(screen.getByRole('button', { name: /Save billing details/i })));

    await waitFor(() => expect(createBillingProfile).toHaveBeenCalledWith(createBillingProfileReq));
  });
  const newOrder = {
    plan: 'professional',
    products: [
      { name: 'mender_standard', quantity: 250, addons: [] },
      { name: 'mender_micro', quantity: 500, addons: [] }
    ]
  };
  it('Allows upgrading subscription', async () => {
    const stripe = loadStripe();

    const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
    const { requestPlanUpgrade } = StoreThunks;
    const ui = (
      <Elements stripe={stripe}>
        <SubscriptionDrawer
          organization={defaultState.organization.organization}
          onClose={vi.fn()}
          plan={PLANS.professional}
          order={newOrder}
          addons={[]}
          isTrial={false}
          currentPlan={PLANS.os}
        />
      </Elements>
    );
    const { rerender } = render(ui, { preloadedState });
    await waitFor(() => rerender(ui));
    expect(screen.getByText(/Upgrade your subscription/i)).toBeVisible();
    expect(screen.getByText(/ok@ok.ok/i)).toBeVisible();
    await waitFor(() => expect(screen.queryByRole('button', { name: /confirm subscription/i })).toBeVisible(), { timeout: TIMEOUTS.fiveSeconds });
    const confirmButton = screen.getByRole('button', { name: /confirm subscription/i });
    await user.click(confirmButton);
    expect(requestPlanUpgrade).toHaveBeenCalledWith(newOrder);
    await act(async () => {
      await vi.advanceTimersByTimeAsync(TIMEOUTS.threeSeconds + 1);
    });
    expect(confirmButton).not.toBeDisabled();
  });
});
