import { Component, Input } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { mock } from "jest-mock-extended";
import { BehaviorSubject, Observable, of } from "rxjs";

import { AccountService, Account } from "@bitwarden/common/auth/abstractions/account.service";
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { FeatureFlag, FeatureFlagValueType } from "@bitwarden/common/enums/feature-flag.enum";
import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { VaultCopyButtonsService } from "@bitwarden/vault";

import { PopupCompactModeService } from "../../../platform/popup/layout/popup-compact-mode.service";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
import { PopupSizeService } from "../../../platform/popup/layout/popup-size.service";

import { AppearanceComponent } from "./appearance.component";

// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
  selector: "popup-header",
  template: `<ng-content></ng-content>`,
})
class MockPopupHeaderComponent {
  // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
  // eslint-disable-next-line @angular-eslint/prefer-signals
  @Input() pageTitle: string;
  // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
  // eslint-disable-next-line @angular-eslint/prefer-signals
  @Input() backAction: () => void;
}

// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
  selector: "popup-page",
  template: `<ng-content></ng-content>`,
})
class MockPopupPageComponent {
  // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
  // eslint-disable-next-line @angular-eslint/prefer-signals
  @Input() loading: boolean;
}

describe("AppearanceComponent", () => {
  let component: AppearanceComponent;
  let fixture: ComponentFixture<AppearanceComponent>;

  const showFavicons$ = new BehaviorSubject<boolean>(true);
  const enableBadgeCounter$ = new BehaviorSubject<boolean>(true);
  const selectedTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Light);
  const enableRoutingAnimation$ = new BehaviorSubject<boolean>(true);
  const enableCompactMode$ = new BehaviorSubject<boolean>(false);
  const showQuickCopyActions$ = new BehaviorSubject<boolean>(false);
  const featureFlag$ = new BehaviorSubject<boolean>(false);
  const hasPremiumFromAnySource$ = new BehaviorSubject<boolean>(false);
  const setSelectedTheme = jest.fn().mockResolvedValue(undefined);
  const setShowFavicons = jest.fn().mockResolvedValue(undefined);
  const setEnableBadgeCounter = jest.fn().mockResolvedValue(undefined);
  const setEnableRoutingAnimation = jest.fn().mockResolvedValue(undefined);
  const setEnableCompactMode = jest.fn().mockResolvedValue(undefined);
  const setShowQuickCopyActions = jest.fn().mockResolvedValue(undefined);
  const setClickItemsToAutofillVaultView = jest.fn().mockResolvedValue(undefined);
  const setShowAtRiskPasswordNotifications = jest.fn().mockResolvedValue(undefined);

  const mockWidthService: Partial<PopupSizeService> = {
    width$: new BehaviorSubject("default"),
    setWidth: jest.fn().mockResolvedValue(undefined),
  };

  beforeEach(async () => {
    setSelectedTheme.mockClear();
    setShowFavicons.mockClear();
    setEnableBadgeCounter.mockClear();
    setEnableRoutingAnimation.mockClear();
    setClickItemsToAutofillVaultView.mockClear();
    setShowAtRiskPasswordNotifications.mockClear();

    const configService = mock<ConfigService>();
    configService.getFeatureFlag$.mockImplementation(
      <Flag extends FeatureFlag>(flag: Flag): Observable<FeatureFlagValueType<Flag>> => {
        if (flag === FeatureFlag.PM31039ItemActionInExtension) {
          return featureFlag$.asObservable() as Observable<FeatureFlagValueType<Flag>>;
        }
        return of(false) as Observable<FeatureFlagValueType<Flag>>;
      },
    );

    await TestBed.configureTestingModule({
      imports: [AppearanceComponent],
      providers: [
        { provide: ConfigService, useValue: configService },
        { provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
        { provide: MessagingService, useValue: mock<MessagingService>() },
        { provide: I18nService, useValue: { t: (key: string) => key } },
        { provide: DomainSettingsService, useValue: { showFavicons$, setShowFavicons } },
        { provide: ThemeStateService, useValue: { selectedTheme$, setSelectedTheme } },
        {
          provide: AnimationControlService,
          useValue: { enableRoutingAnimation$, setEnableRoutingAnimation },
        },
        {
          provide: BadgeSettingsServiceAbstraction,
          useValue: { enableBadgeCounter$, setEnableBadgeCounter },
        },
        {
          provide: PopupCompactModeService,
          useValue: { enabled$: enableCompactMode$, setEnabled: setEnableCompactMode },
        },
        {
          provide: VaultCopyButtonsService,
          useValue: {
            showQuickCopyActions$,
            setShowQuickCopyActions,
          } as Partial<VaultCopyButtonsService>,
        },
        {
          provide: PopupSizeService,
          useValue: mockWidthService,
        },
        {
          provide: VaultSettingsService,
          useValue: {
            clickItemsToAutofillVaultView$: of(false),
            showAtRiskPasswordNotifications$: of(true),
            setClickItemsToAutofillVaultView,
            setShowAtRiskPasswordNotifications,
          },
        },
        {
          provide: AccountService,
          useValue: {
            activeAccount$: new BehaviorSubject({
              id: "test-user-id",
            } as Account),
          },
        },
        {
          provide: BillingAccountProfileStateService,
          useValue: {
            hasPremiumFromAnySource$: jest.fn().mockReturnValue(hasPremiumFromAnySource$),
          },
        },
      ],
    })
      .overrideComponent(AppearanceComponent, {
        remove: {
          imports: [PopupHeaderComponent, PopupPageComponent],
        },
        add: {
          imports: [MockPopupHeaderComponent, MockPopupPageComponent],
        },
      })
      .compileComponents();

    fixture = TestBed.createComponent(AppearanceComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it("populates the form with the user's current settings", async () => {
    fixture.detectChanges();
    await fixture.whenStable();
    fixture.detectChanges();
    expect(component.appearanceForm.value).toEqual({
      enableAnimations: true,
      enableFavicon: true,
      enableBadgeCounter: true,
      theme: ThemeType.Light,
      enableCompactMode: false,
      showQuickCopyActions: false,
      width: "default",
      clickItemsToAutofillVaultView: false,
      showAtRiskNotifications: true,
    });
  });

  describe("form changes", () => {
    it("updates the users theme", () => {
      component.appearanceForm.controls.theme.setValue(ThemeType.Light);

      expect(setSelectedTheme).toHaveBeenCalledWith(ThemeType.Light);
    });

    it("updates the users favicon setting", () => {
      component.appearanceForm.controls.enableFavicon.setValue(false);

      expect(setShowFavicons).toHaveBeenCalledWith(false);
    });

    it("updates the users badge counter setting", () => {
      component.appearanceForm.controls.enableBadgeCounter.setValue(false);

      expect(setEnableBadgeCounter).toHaveBeenCalledWith(false);
    });

    it("updates the animation setting", () => {
      component.appearanceForm.controls.enableAnimations.setValue(false);

      expect(setEnableRoutingAnimation).toHaveBeenCalledWith(false);
    });

    it("updates the compact mode setting", () => {
      component.appearanceForm.controls.enableCompactMode.setValue(true);

      expect(setEnableCompactMode).toHaveBeenCalledWith(true);
    });

    it("updates the quick copy actions setting", () => {
      component.appearanceForm.controls.showQuickCopyActions.setValue(true);

      expect(setShowQuickCopyActions).toHaveBeenCalledWith(true);
    });

    it("updates the width setting", () => {
      component.appearanceForm.controls.width.setValue("wide");

      expect(mockWidthService.setWidth).toHaveBeenCalledWith("wide");
    });
  });

  describe("PM31039ItemActionInExtension feature flag", () => {
    describe("when set to OFF", () => {
      it("should show clickItemsToAutofillVaultView checkbox", () => {
        featureFlag$.next(false);
        fixture.detectChanges();

        const checkbox = fixture.debugElement.query(
          By.css('input[formControlName="clickItemsToAutofillVaultView"]'),
        );
        expect(checkbox).not.toBeNull();
      });

      it("should update the clickItemsToAutofillVaultView setting when changed", () => {
        featureFlag$.next(false);
        fixture.detectChanges();

        component.appearanceForm.controls.clickItemsToAutofillVaultView.setValue(true);

        expect(setClickItemsToAutofillVaultView).toHaveBeenCalledWith(true);
      });
    });

    describe("when set to ON", () => {
      it("should hide clickItemsToAutofillVaultView checkbox", () => {
        featureFlag$.next(true);
        fixture.detectChanges();

        const checkbox = fixture.debugElement.query(
          By.css('input[formControlName="clickItemsToAutofillVaultView"]'),
        );
        expect(checkbox).toBeNull();
      });
    });
  });

  describe("showAtRiskNotifications", () => {
    it("updates the showAtRiskPasswordNotifications setting when changed", async () => {
      await fixture.whenStable();

      component.appearanceForm.controls.showAtRiskNotifications.setValue(false);

      expect(setShowAtRiskPasswordNotifications).toHaveBeenCalledWith(false);
    });

    it("shows the showAtRiskNotifications checkbox only for premium users", async () => {
      hasPremiumFromAnySource$.next(true);
      fixture.detectChanges();
      await fixture.whenStable();
      fixture.detectChanges();

      const checkbox = fixture.debugElement.query(
        By.css('input[formControlName="showAtRiskNotifications"]'),
      );
      expect(checkbox).not.toBeNull();
    });

    it("hides the showAtRiskNotifications checkbox for non-premium users", async () => {
      hasPremiumFromAnySource$.next(false);
      fixture.detectChanges();
      await fixture.whenStable();
      fixture.detectChanges();

      const checkbox = fixture.debugElement.query(
        By.css('input[formControlName="showAtRiskNotifications"]'),
      );
      expect(checkbox).toBeNull();
    });
  });
});
