import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  linkedSignal,
} from '@angular/core';
import { injectTabId } from '@isa/core/tabs';

import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import {
  FilterMenuButtonComponent,
  FilterService,
  SearchBarInputComponent,
  OrderByToolbarComponent,
} from '@isa/shared/filter';

import { IconButtonComponent } from '@isa/ui/buttons';
import { EmptyStateComponent } from '@isa/ui/empty-state';
import { provideIcons } from '@ng-icons/core';
import { isaActionSort, isaActionFilter } from '@isa/icons';
import {
  ReceiptListItem,
  ReturnSearchStatus,
  ReturnSearchStore,
} from '@isa/oms/data-access';
import { ReturnSearchResultItemComponent } from './return-search-result-item/return-search-result-item.component';
import { Breakpoint, InViewportDirective } from '@isa/ui/layout';
import { CallbackResult, ListResponseArgs } from '@isa/common/data-access';
import { injectRestoreScrollPosition } from '@isa/utils/scroll-position';
import { breakpoint } from '@isa/ui/layout';

/**
 * Component responsible for displaying return search results.
 *
 * This component handles:
 * - Displaying a list of return search results
 * - Filtering and sorting results
 * - Searching for returns
 * - Pagination with infinite scrolling
 * - Responsive layout changes based on device size
 */
@Component({
  selector: 'oms-feature-return-search-result',
  templateUrl: './return-search-result.component.html',
  styleUrls: ['./return-search-result.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    RouterLink,
    ReturnSearchResultItemComponent,
    OrderByToolbarComponent,
    IconButtonComponent,
    SearchBarInputComponent,
    EmptyStateComponent,
    FilterMenuButtonComponent,
    InViewportDirective,
  ],
  providers: [provideIcons({ isaActionSort, isaActionFilter })],
})
export class ReturnSearchResultComponent implements AfterViewInit {
  /** Route service for navigation and route information */
  #route = inject(ActivatedRoute);

  /** Router service for programmatic navigation */
  #router = inject(Router);

  /** Service for managing filters and search queries */
  #filterService = inject(FilterService);

  /** Utility for restoring scroll position when returning to this view */
  restoreScrollPosition = injectRestoreScrollPosition();

  /** Current process ID from the activated route */
  processId = injectTabId();

  /** Store for managing return search data and operations */
  returnSearchStore = inject(ReturnSearchStore);

  /** Enum reference for template usage */
  ReturnSearchStatus = ReturnSearchStatus;

  /** Signal tracking whether the viewport is at tablet size or above */
  mobileBreakpoint = breakpoint([Breakpoint.Tablet]);

  /**
   * Signal controlling the visibility of the order-by toolbar on mobile
   * Initially shows toolbar when NOT on mobile
   */
  showOrderByToolbarMobile = linkedSignal(() => !this.mobileBreakpoint());

  /**
   * Computes the current return search entity based on the active process ID
   * @returns The return search entity or undefined if no process ID is available
   */
  entity = computed(() => {
    const processId = this.processId();
    if (processId) {
      return this.returnSearchStore.getEntity(processId);
    }
    return undefined;
  });

  /**
   * Returns the list of return items from the current entity
   * @returns Array of return items or empty array if none available
   */
  entityItems = computed(() => {
    return this.entity()?.items ?? [];
  });

  /**
   * Returns the total number of hits from the search results
   * @returns Total hits or 0 if no data available
   */
  entityHits = computed(() => {
    return this.entity()?.hits ?? 0;
  });

  /**
   * Returns the current status of the search operation
   * @returns Current status or Idle if no entity is available
   */
  entityStatus = computed(() => {
    return this.entity()?.status ?? ReturnSearchStatus.Idle;
  });

  /**
   * Determines whether to render the item list based on available items
   * @returns Boolean indicating if items are available to display
   */
  renderItemList = computed(() => {
    return this.entityItems().length;
  });

  /**
   * Determines whether to show pagination loading indicator
   * @returns Boolean indicating if pagination loading should be shown
   */
  renderPagingLoader = computed(() => {
    return this.entityStatus() === ReturnSearchStatus.Pending;
  });

  /**
   * Determines whether to show the main search loading indicator
   * Shows loader only when search is pending and no items are available yet
   * @returns Boolean indicating if search loading should be shown
   */
  renderSearchLoader = computed(() => {
    return (
      this.entityStatus() === ReturnSearchStatus.Pending &&
      this.entityItems().length === 0
    );
  });

  /**
   * Determines whether to render the page trigger for infinite scrolling
   * Triggers pagination when more results are available than currently loaded
   * @returns Boolean indicating if page trigger should be shown
   */
  renderPageTrigger = computed(() => {
    const entity = this.entity();
    if (!entity || entity.status === ReturnSearchStatus.Pending) return false;

    const { hits, items } = entity;
    return Boolean(hits && Array.isArray(items) && hits > items.length);
  });

  /**
   * Lifecycle hook called after the component's view has been initialized
   * Restores scroll position when returning to this view
   */
  ngAfterViewInit(): void {
    this.restoreScrollPosition();
  }

  /**
   * Initiates a search operation with the current filter settings
   * Navigates directly to the receipt if only one result is found
   */
  search() {
    const processId = this.processId();
    if (processId) {
      this.#filterService.commit();

      this.returnSearchStore.search({
        processId,
        query: this.#filterService.query(),
        clear: true,
        cb: this.searchCb,
      });
    }
  }

  /**
   * Callback function for search operations
   * Automatically navigates to the receipt detail view if exactly one result is found
   * @param result The callback result containing search data
   */
  searchCb = ({ data }: CallbackResult<ListResponseArgs<ReceiptListItem>>) => {
    if (data) {
      if (data.result.length === 1) {
        this.navigate(['../receipt', data.result[0].id]);
      }
    }
  };

  /**
   * Handles infinite scrolling pagination when the page trigger enters the viewport
   * Loads more results when triggered
   * @param inViewport Boolean indicating if the trigger element is in viewport
   */
  paging(inViewport: boolean) {
    if (!inViewport) {
      return;
    }

    const processId = this.processId();
    if (processId) {
      this.returnSearchStore.search({
        processId,
        clear: false,
        query: this.#filterService.query(),
      });
    }
  }

  /**
   * Navigates to a specified path while preserving filter query parameters
   * @param path Array of path segments for navigation
   */
  navigate(path: (string | number)[]) {
    this.#router.navigate(path, {
      relativeTo: this.#route,
      queryParams: this.#filterService.queryParams(),
    });
  }
}
