// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import type { ReactNode } from 'react';
import React from 'react';
import lodash from 'lodash';

import type { ToFindType } from './LeftPaneHelper.dom.js';
import { LeftPaneHelper } from './LeftPaneHelper.dom.js';
import { getConversationInDirection } from './getConversationInDirection.dom.js';
import type { Row } from '../ConversationList.dom.js';
import { RowType } from '../ConversationList.dom.js';
import type { PropsData as ConversationListItemPropsType } from '../conversationList/ConversationListItem.dom.js';
import type { LocalizerType } from '../../types/Util.std.js';
import type {
  ConversationType,
  ShowConversationType,
} from '../../state/ducks/conversations.preload.js';
import { LeftPaneSearchInput } from '../LeftPaneSearchInput.dom.js';
import type { LeftPaneSearchPropsType } from './LeftPaneSearchHelper.dom.js';
import { LeftPaneSearchHelper } from './LeftPaneSearchHelper.dom.js';
import * as KeyboardLayout from '../../services/keyboardLayout.dom.js';

const { last } = lodash;

type LeftPaneArchiveBasePropsType = {
  archivedConversations: ReadonlyArray<ConversationListItemPropsType>;
  isSearchingGlobally: boolean;
  searchConversation: undefined | ConversationType;
  searchTerm: string;
  startSearchCounter: number;
};

export type LeftPaneArchivePropsType =
  | LeftPaneArchiveBasePropsType
  | (LeftPaneArchiveBasePropsType & LeftPaneSearchPropsType);

export class LeftPaneArchiveHelper extends LeftPaneHelper<LeftPaneArchivePropsType> {
  readonly #archivedConversations: ReadonlyArray<ConversationListItemPropsType>;
  readonly #isSearchingGlobally: boolean;
  readonly #searchConversation: undefined | ConversationType;
  readonly #searchTerm: string;
  readonly #searchHelper: undefined | LeftPaneSearchHelper;
  readonly #startSearchCounter: number;

  constructor(props: Readonly<LeftPaneArchivePropsType>) {
    super();

    this.#archivedConversations = props.archivedConversations;
    this.#isSearchingGlobally = props.isSearchingGlobally;
    this.#searchConversation = props.searchConversation;
    this.#searchTerm = props.searchTerm;
    this.#startSearchCounter = props.startSearchCounter;

    if ('conversationResults' in props) {
      this.#searchHelper = new LeftPaneSearchHelper(props);
    }
  }

  override getHeaderContents({
    i18n,
    showInbox,
  }: Readonly<{
    i18n: LocalizerType;
    showInbox: () => void;
  }>): ReactNode {
    return (
      <div className="module-left-pane__header__contents">
        <button
          onClick={this.getBackAction({ showInbox })}
          className="module-left-pane__header__contents__back-button"
          title={i18n('icu:backToInbox')}
          aria-label={i18n('icu:backToInbox')}
          type="button"
        />
        <div className="module-left-pane__header__contents__text">
          {i18n('icu:archivedConversations')}
        </div>
      </div>
    );
  }

  override getSearchInput({
    clearConversationSearch,
    clearSearchQuery,
    endConversationSearch,
    endSearch,
    i18n,
    updateSearchTerm,
    showConversation,
  }: Readonly<{
    clearConversationSearch: () => unknown;
    clearSearchQuery: () => unknown;
    endConversationSearch: () => unknown;
    endSearch: () => unknown;
    i18n: LocalizerType;
    updateSearchTerm: (searchTerm: string) => unknown;
    showConversation: ShowConversationType;
  }>): ReactNode | null {
    if (!this.#searchConversation) {
      return null;
    }

    return (
      <LeftPaneSearchInput
        clearConversationSearch={clearConversationSearch}
        clearSearchQuery={clearSearchQuery}
        endConversationSearch={endConversationSearch}
        endSearch={endSearch}
        i18n={i18n}
        isSearchingGlobally={this.#isSearchingGlobally}
        searchConversation={this.#searchConversation}
        searchTerm={this.#searchTerm}
        showConversation={showConversation}
        startSearchCounter={this.#startSearchCounter}
        updateSearchTerm={updateSearchTerm}
      />
    );
  }

  override getBackAction({ showInbox }: { showInbox: () => void }): () => void {
    return showInbox;
  }

  override getPreRowsNode({
    i18n,
  }: Readonly<{ i18n: LocalizerType }>): ReactNode | null {
    if (this.#searchHelper) {
      return this.#searchHelper.getPreRowsNode({ i18n });
    }

    return (
      <div className="module-left-pane__archive-helper-text">
        {this.getRowCount() > 0
          ? i18n('icu:archiveHelperText')
          : i18n('icu:noArchivedConversations')}
      </div>
    );
  }

  getRowCount(): number {
    return (
      this.#searchHelper?.getRowCount() ?? this.#archivedConversations.length
    );
  }

  getRow(rowIndex: number): undefined | Row {
    if (this.#searchHelper) {
      return this.#searchHelper.getRow(rowIndex);
    }

    const conversation = this.#archivedConversations[rowIndex];
    return conversation
      ? {
          type: RowType.Conversation,
          conversation,
        }
      : undefined;
  }

  override getRowIndexToScrollTo(
    selectedConversationId: undefined | string
  ): undefined | number {
    if (this.#searchHelper) {
      return this.#searchHelper.getRowIndexToScrollTo(selectedConversationId);
    }

    if (!selectedConversationId) {
      return undefined;
    }
    const result = this.#archivedConversations.findIndex(
      conversation => conversation.id === selectedConversationId
    );
    return result === -1 ? undefined : result;
  }

  getConversationAndMessageAtIndex(
    conversationIndex: number
  ): undefined | { conversationId: string } {
    const searchHelper = this.#searchHelper;
    const archivedConversations = this.#archivedConversations;

    if (searchHelper) {
      return searchHelper.getConversationAndMessageAtIndex(conversationIndex);
    }

    const conversation =
      archivedConversations[conversationIndex] || last(archivedConversations);
    return conversation ? { conversationId: conversation.id } : undefined;
  }

  getConversationAndMessageInDirection(
    toFind: Readonly<ToFindType>,
    selectedConversationId: undefined | string,
    targetedMessageId: unknown
  ): undefined | { conversationId: string } {
    if (this.#searchHelper) {
      return this.#searchHelper.getConversationAndMessageInDirection(
        toFind,
        selectedConversationId,
        targetedMessageId
      );
    }

    return getConversationInDirection(
      this.#archivedConversations,
      toFind,
      selectedConversationId
    );
  }

  override requiresFullWidth(): boolean {
    const hasNoConversations = !this.#archivedConversations.length;
    return hasNoConversations;
  }

  shouldRecomputeRowHeights(old: Readonly<LeftPaneArchivePropsType>): boolean {
    const hasSearchingChanged =
      'conversationResults' in old !== Boolean(this.#searchHelper);
    if (hasSearchingChanged) {
      return true;
    }

    if ('conversationResults' in old && this.#searchHelper) {
      return this.#searchHelper.shouldRecomputeRowHeights(old);
    }

    return false;
  }

  override onKeyDown(
    event: KeyboardEvent,
    {
      searchInConversation,
      selectedConversationId,
    }: Readonly<{
      searchInConversation: (conversationId: string) => unknown;
      selectedConversationId: undefined | string;
    }>
  ): void {
    if (!selectedConversationId) {
      return;
    }

    const { ctrlKey, metaKey, shiftKey } = event;
    const commandKey = window.platform === 'darwin' && metaKey;
    const controlKey = window.platform !== 'darwin' && ctrlKey;
    const commandOrCtrl = commandKey || controlKey;
    const commandAndCtrl = commandKey && ctrlKey;
    const key = KeyboardLayout.lookup(event);

    if (
      commandOrCtrl &&
      !commandAndCtrl &&
      shiftKey &&
      (key === 'f' || key === 'F') &&
      this.#archivedConversations.some(
        ({ id }) => id === selectedConversationId
      )
    ) {
      searchInConversation(selectedConversationId);

      event.preventDefault();
      event.stopPropagation();
    }
  }
}
