/**
 * Copyright IBM Corp. 2016, 2025
 * SPDX-License-Identifier: BUSL-1.1
 */

import { service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import Component from '@glimmer/component';
import keys from 'core/utils/keys';
import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard';
import errorMessage from 'vault/utils/error-message';

import type ApiService from 'vault/services/api';
import type FlagsService from 'vault/services/flags';
import type FlashMessageService from 'vault/services/flash-messages';
import type NamespaceService from 'vault/services/namespace';
import type RouterService from '@ember/routing/router-service';
import type WizardService from 'vault/services/wizard';
import type { HTMLElementEvent } from 'vault/forms';
import type { PaginatedMetadata } from 'core/utils/paginate-list';

/**
 * @module PageNamespaces
 * PageNamespaces component handles the display and management of namespaces,
 * including the namespace wizard for first-time users.
 *
 * @param {object} namespaces - list of namespaces
 * @param {string} pageFilter - current page filter value
 * @param {function} onFilterChange - callback function to handle filter changes, receives filter string or null to clear
 * @param {function} onRefresh - callback function to refresh the namespace list from the route/controller
 */

interface Args {
  model: {
    namespaces: NamespaceModel[] & PaginatedMetadata;
    pageFilter: string | null;
  };
  onFilterChange: CallableFunction;
  onRefresh: CallableFunction;
}

interface NamespaceModel {
  id: string;
  destroyRecord: () => Promise<void>;
  [key: string]: unknown;
}

export default class PageNamespacesComponent extends Component<Args> {
  @service declare readonly api: ApiService;
  @service declare readonly router: RouterService;
  @service declare readonly flags: FlagsService;
  @service declare readonly flashMessages: FlashMessageService;
  @service declare readonly wizard: WizardService;
  @service declare namespace: NamespaceService;

  // The `query` property is used to track the filter
  // input value separately from updating the `pageFilter`
  // browser query param to prevent unnecessary re-renders.
  @tracked query;
  @tracked nsToDelete = null;
  @tracked showSetupAlert = false;
  @tracked shouldRenderIntroModal = false;

  wizardId = WIZARD_ID_MAP.namespace;

  tableColumns = [
    {
      key: 'id',
      label: 'Path',
    },
    {
      key: 'popupMenu',
      label: 'Action',
      width: '75px',
    },
  ];

  constructor(owner: unknown, args: Args) {
    super(owner, args);
    this.query = this.args.model.pageFilter || '';
  }

  get namespaceIds() {
    return this.args.model.namespaces.map((namespace) => {
      return { id: namespace.id };
    });
  }

  // show the full available namespace path e.g. "root/ns1/child2", "admin/ns1/child2"
  get namespacePath() {
    if (this.namespace.inRootNamespace) {
      return 'root';
    }

    // For nested namespaces, show "root/" prefix if not HVD managed and no separate user root
    if (!this.namespace.userRootNamespace && !this.flags.isHvdManaged) {
      return `root/${this.namespace.path}`;
    }

    // If there is a userRootNamespace or it is HVD managed, then the path alone will suffice
    return this.namespace.path;
  }

  // Use a getter here as total is undefined instead of 0, but checking for > 0 will cover both cases.
  get hasNamespaces() {
    const { namespaces } = this.args.model;
    return namespaces.meta?.total > 0;
  }

  // Show header and breadcrumbs when viewing the intro page or during the list view.
  // Do not show during Guided Start as that has its own header
  get showPageHeader() {
    return !this.showWizard || this.wizard.isIntroVisible(this.wizardId);
  }

  get showContent() {
    // Show when the 1) wizard is not shown OR 2) wizard intro modal is shown
    // This ensures the wizard intro modal is shown on top of the list view and the background content is not blank behind the modal
    return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(this.wizardId));
  }

  get showIntroButton() {
    return this.showContent && !this.hasNamespaces;
  }

  get showWizard() {
    // Show when there are no existing namespaces and it is not in a dismissed state
    return !this.wizard.isDismissed(this.wizardId) && !this.hasNamespaces;
  }

  @action
  handleKeyDown(event: KeyboardEvent) {
    const isEscKeyPressed = keys.ESC.includes(event.key);
    if (isEscKeyPressed) {
      // On escape, clear the filter
      this.args.onFilterChange(null);
    }
    // ignore all other key events
  }

  @action
  handleInput(evt: HTMLElementEvent<HTMLInputElement>) {
    this.query = evt.target.value;
  }

  @action
  handleSearch(evt: HTMLElementEvent<HTMLInputElement>) {
    evt.preventDefault();
    this.args.onFilterChange(this.query);
  }

  @action
  async deleteNamespace(namespaceId: string) {
    const nsToDelete = this.args.model.namespaces.find((ns) => ns.id === namespaceId) as NamespaceModel;
    try {
      // Attempt to destroy the record
      await nsToDelete.destroyRecord();

      // Log success and optionally update the UI
      this.flashMessages.success(`Successfully deleted namespace: ${nsToDelete.id}`);

      // Call the refresh method to update the list
      this.refreshNamespaceList();
    } catch (error) {
      const message = errorMessage(error);
      this.flashMessages.danger(message);
    }
    this.nsToDelete = null;
  }

  @action
  async refreshNamespaceList() {
    try {
      // Await the async operation to complete
      await this.namespace.findNamespacesForUser.perform();
      this.args.onRefresh();
    } catch (error) {
      this.flashMessages.danger('There was an error refreshing the namespace list.');
    }
  }

  @action
  showIntroPage() {
    // Reset the wizard dismissal state to allow re-entering the wizard
    this.wizard.reset(this.wizardId);
    this.shouldRenderIntroModal = true;
  }

  @action handlePageChange() {
    this.args.onRefresh();
  }

  @action
  switchNamespace(targetNamespace: string) {
    this.router.transitionTo('vault.cluster.dashboard', {
      queryParams: { namespace: targetNamespace },
    });
  }

  async createNamespace(path: string, header?: string) {
    const headers = header ? this.api.buildHeaders({ namespace: header }) : undefined;
    await this.api.sys.systemWriteNamespacesPath(path, {}, headers);
  }
}
