import { Component } from "@angular/core";
import { TestBed } from "@angular/core/testing";
import { provideRouter } from "@angular/router";
import { RouterTestingHarness } from "@angular/router/testing";
import { MockProxy, any, mock } from "jest-mock-extended";

import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationUserType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { DialogService } from "@bitwarden/components";

import { isEnterpriseOrgGuard } from "./is-enterprise-org.guard";

@Component({
  template: "<h1>This is the home screen!</h1>",
})
export class HomescreenComponent {}

@Component({
  template: "<h1>This component can only be accessed by a enterprise organization!</h1>",
})
export class IsEnterpriseOrganizationComponent {}

@Component({
  template: "<h1>This is the organization upgrade screen!</h1>",
})
export class OrganizationUpgradeScreenComponent {}

const orgFactory = (props: Partial<Organization> = {}) =>
  Object.assign(
    new Organization(),
    {
      id: "myOrgId",
      enabled: true,
      type: OrganizationUserType.Admin,
    },
    props,
  );

describe("Is Enterprise Org Guard", () => {
  let organizationService: MockProxy<OrganizationService>;
  let dialogService: MockProxy<DialogService>;
  let routerHarness: RouterTestingHarness;

  beforeEach(async () => {
    organizationService = mock<OrganizationService>();
    dialogService = mock<DialogService>();

    TestBed.configureTestingModule({
      providers: [
        { provide: OrganizationService, useValue: organizationService },
        { provide: DialogService, useValue: dialogService },
        provideRouter([
          {
            path: "",
            component: HomescreenComponent,
          },
          {
            path: "organizations/:organizationId/enterpriseOrgsOnly",
            component: IsEnterpriseOrganizationComponent,
            canActivate: [isEnterpriseOrgGuard()],
          },
          {
            path: "organizations/:organizationId/billing/subscription",
            component: OrganizationUpgradeScreenComponent,
          },
        ]),
      ],
    });

    routerHarness = await RouterTestingHarness.create();
  });

  it("redirects to `/` if the organization id provided is not found", async () => {
    const org = orgFactory();
    organizationService.get.calledWith(org.id).mockResolvedValue(null);
    await routerHarness.navigateByUrl(`organizations/${org.id}/enterpriseOrgsOnly`);
    expect(routerHarness.routeNativeElement?.querySelector("h1")?.textContent?.trim() ?? "").toBe(
      "This is the home screen!",
    );
  });

  it.each([
    ProductTierType.Free,
    ProductTierType.Families,
    ProductTierType.Teams,
    ProductTierType.TeamsStarter,
  ])(
    "shows a dialog to users of a not enterprise organization and does not proceed with navigation for productTierType '%s'",
    async (productTierType) => {
      const org = orgFactory({
        type: OrganizationUserType.User,
        productTierType: productTierType,
      });
      organizationService.get.calledWith(org.id).mockResolvedValue(org);
      await routerHarness.navigateByUrl(`organizations/${org.id}/enterpriseOrgsOnly`);
      expect(dialogService.openSimpleDialog).toHaveBeenCalled();
      expect(
        routerHarness.routeNativeElement?.querySelector("h1")?.textContent?.trim() ?? "",
      ).not.toBe("This component can only be accessed by a enterprise organization!");
    },
  );

  it("redirects users with billing access to the billing screen to upgrade", async () => {
    const org = orgFactory({
      type: OrganizationUserType.Owner,
      productTierType: ProductTierType.Teams,
    });
    organizationService.get.calledWith(org.id).mockResolvedValue(org);
    dialogService.openSimpleDialog.calledWith(any()).mockResolvedValue(true);
    await routerHarness.navigateByUrl(`organizations/${org.id}/enterpriseOrgsOnly`);
    expect(routerHarness.routeNativeElement?.querySelector("h1")?.textContent?.trim() ?? "").toBe(
      "This is the organization upgrade screen!",
    );
  });

  it("proceeds with navigation if the organization in question is a enterprise organization", async () => {
    const org = orgFactory({ productTierType: ProductTierType.Enterprise });
    organizationService.get.calledWith(org.id).mockResolvedValue(org);
    await routerHarness.navigateByUrl(`organizations/${org.id}/enterpriseOrgsOnly`);
    expect(routerHarness.routeNativeElement?.querySelector("h1")?.textContent?.trim() ?? "").toBe(
      "This component can only be accessed by a enterprise organization!",
    );
  });
});
