/**
 * Copyright (C) 2018-2026  The Software Heritage developers
 * See the AUTHORS file at the top-level directory of this distribution
 * License: GNU Affero General Public License version 3, or any later version
 * See top-level LICENSE file for more information
 */

import {errorMessageFromResponse, handleFetchError, isArchivedOrigin} from 'utils/functions';

const limit = 100;
const linksPrev = [];
let linkNext = null;
let linkCurrent = null;
let inSearch = false;

function parseLinkHeader(s) {
  const re = /<(.+)>; rel="next"/;
  return s.match(re)[1];
}

function fixTableRowsStyle() {
  setTimeout(() => {
    $('#origin-search-results tbody tr').removeAttr('style');
  });
}

function clearOriginSearchResultsTable() {
  $('#origin-search-results tbody tr').remove();
}

async function populateOriginSearchResultsTable(origins) {
  const visitType = $('#swh-search-visit-type').val();
  if (origins.length > 0) {
    $('#swh-origin-search-results').show();
    $('#swh-no-result').hide();
    clearOriginSearchResultsTable();
    const table = $('#origin-search-results tbody');
    for (const origin of origins) {
      let visitTypes = origin.visit_types;
      if (visitTypes && visitTypes.includes(visitType)) {
        visitTypes = [visitType];
      } else if (!visitTypes) {
        visitTypes = ['???'];
      }
      for (const oVisitType of visitTypes) {
        let browseUrl = `${Urls.browse_origin()}?origin_url=${encodeURIComponent(origin.url)}`;
        if (oVisitType !== '???') {
          browseUrl += `&visit_type=${oVisitType}`;
        }
        let tableRow = '<tr class="swh-search-result-entry swh-tr-hover-highlight">';
        tableRow += `<td class="swh-origin-visit-type" style="width: 120px;">${oVisitType}</td>`;
        tableRow += `<td style="white-space: nowrap;"><a href="${browseUrl}">${origin.url}</a></td>`;
        let archivingStatus = '???';
        if (origin.hasOwnProperty('snapshot_id') && origin.snapshot_id !== null) {
          archivingStatus = '<i title="Software origin has been archived by Software Heritage" ' +
          'class="mdi mdi-check-bold mdi-fw"></i>Archived';
        } else if (origin.hasOwnProperty('snapshot_id') && origin.snapshot_id == null) {
          archivingStatus = '<i title="Software origin archival by Software Heritage is pending" ' +
          'class="mdi mdi-close-thick mdi-fw"></i>Pending archival';
        }
        tableRow += `<td class="swh-visit-status">${archivingStatus}</td>`;
        tableRow += '</tr>';
        table.append(tableRow);
      }
    }

    fixTableRowsStyle();
  } else {
    $('#swh-origin-search-results').hide();
    $('#swh-no-result').text('No origins matching the search criteria were found.');
    $('#swh-no-result').show();
  }

  if (linkNext === null) {
    $('#origins-next-results-button').addClass('disabled');
    $('#origins-next-results-button').attr('tabindex', '-1');
  } else {
    $('#origins-next-results-button').removeClass('disabled');
    $('#origins-next-results-button').attr('tabindex', '0');
  }

  if (linksPrev.length === 0) {
    $('#origins-prev-results-button').addClass('disabled');
    $('#origins-prev-results-button').attr('tabindex', '-1');
  } else {
    $('#origins-prev-results-button').removeClass('disabled');
    $('#origins-prev-results-button').attr('tabindex', '0');
  }

  inSearch = false;
  setTimeout(() => {
    window.scrollTo(0, 0);
  });
}

function searchOriginsFirst(searchQueryText, limit) {
  let baseSearchUrl;
  const searchMetadata = $('#swh-search-origin-metadata').prop('checked');
  if (searchMetadata) {
    baseSearchUrl = new URL(Urls.api_1_origin_metadata_search(), window.location);
    baseSearchUrl.searchParams.append('fulltext', searchQueryText);
  } else {
    const useSearchQL = $('#swh-search-use-ql').prop('checked');
    baseSearchUrl = new URL(Urls.api_1_origin_search(searchQueryText.replace('?', '%3F')), window.location);
    baseSearchUrl.searchParams.append('use_ql', useSearchQL ?? false);
  }

  // Return only relevant fields
  baseSearchUrl.searchParams.append('fields', 'url,visit_types,snapshot_id');

  const withVisit = $('#swh-search-origins-with-visit').prop('checked');
  const withContent = $('#swh-filter-empty-visits').prop('checked');
  baseSearchUrl.searchParams.append('limit', limit);
  baseSearchUrl.searchParams.append('with_visit', withVisit);
  baseSearchUrl.searchParams.append('with_content', withContent);
  const visitType = $('#swh-search-visit-type').val();
  if (visitType !== 'any') {
    baseSearchUrl.searchParams.append('visit_type', visitType);
  }
  const searchUrl = baseSearchUrl.toString();
  searchOrigins(searchUrl);
}

async function searchOrigins(searchUrl) {
  clearOriginSearchResultsTable();
  $('.swh-loading').addClass('show');
  try {
    const response = await fetch(searchUrl);
    handleFetchError(response);
    const data = await response.json();
    // Save link to the current results page
    linkCurrent = searchUrl;
    // Save link to the next results page.
    linkNext = null;
    if (response.headers.has('Link')) {
      const parsedLink = parseLinkHeader(response.headers.get('Link'));
      if (parsedLink !== undefined) {
        linkNext = parsedLink;
      }
    }
    if (response.headers.has('X-Total-Count')) {
      let totalResults = response.headers.get('X-Total-Count');
      if (totalResults != null) {
        totalResults = new Intl.NumberFormat('en-US').format(totalResults);
        let text = '';
        if (totalResults) {
          text = `${totalResults} origins found`;
        }
        $('#swh-origin-search-total-results').text(text);
      }
    }
    // prevLinks is updated by the caller, which is the one to know if
    // we're going forward or backward in the pages.

    $('.swh-loading').removeClass('show');
    populateOriginSearchResultsTable(data);
  } catch (errorResponse) {
    const errorData = await errorResponse.json();
    $('.swh-loading').removeClass('show');
    inSearch = false;
    $('#swh-origin-search-results').hide();
    $('#swh-no-result').text(errorMessageFromResponse(
      errorData, 'An unknown error occurred while searching origins'));
    $('#swh-no-result').show();
  }
}

async function doSearch() {
  $('#swh-no-result').hide();
  const searchQueryText = $('#swh-origins-url-patterns').val();
  const visitType = $('#swh-search-visit-type').val();
  inSearch = true;
  if (searchQueryText.startsWith('swh:')) {
    try {
      // searchQueryText may be a PID so sending search queries to PID resolve endpoint
      const resolveSWHIDUrl = Urls.api_1_resolve_swhid(searchQueryText);
      const response = await fetch(resolveSWHIDUrl);
      handleFetchError(response);
      const data = await response.json();
      // SWHID has been successfully resolved,
      // so redirect to browse page
      window.location = data.browse_url;
    } catch (response) {
      // display a useful error message if the input
      // looks like a SWHID
      const data = await response.json();
      $('#swh-origin-search-results').hide();
      $('.swh-search-pagination').hide();
      $('#swh-no-result').text(data.reason);
      $('#swh-no-result').show();
    }
  } else if (await isArchivedOrigin(searchQueryText, visitType)) {
    // redirect to the browse origin
    let browseOriginUrl =
      `${Urls.browse_origin()}?origin_url=${encodeURIComponent(searchQueryText)}`;
    if (visitType && visitType !== 'any') {
      browseOriginUrl += `&visit_type=${visitType}`;
    }
    window.location.href = browseOriginUrl;
  } else {
    // otherwise, proceed with origins search irrespective of the error
    $('#swh-origin-search-results').show();
    $('.swh-search-pagination').show();
    searchOriginsFirst(searchQueryText, limit);
  }
}

export function initOriginSearch() {
  $(() => {
    $('#swh-search-origins').on('submit', event => {
      event.preventDefault();
      if (event.target.checkValidity()) {
        $(event.target).removeClass('was-validated');
        const searchQueryText = $('#swh-origins-url-patterns').val().trim();
        const withVisit = $('#swh-search-origins-with-visit').prop('checked');
        const withContent = $('#swh-filter-empty-visits').prop('checked');
        const useSearchQL = $('#swh-search-use-ql').prop('checked');
        const searchMetadata = $('#swh-search-origin-metadata').prop('checked');
        const visitType = $('#swh-search-visit-type').val();
        const queryParameters = new URLSearchParams();
        queryParameters.append('q', searchQueryText);
        if (withVisit) {
          queryParameters.append('with_visit', withVisit);
        }
        if (withContent) {
          queryParameters.append('with_content', withContent);
        }
        if (useSearchQL) {
          queryParameters.append('use_ql', useSearchQL ?? false);
        }
        if (searchMetadata) {
          queryParameters.append('search_metadata', searchMetadata);
        }
        if (visitType !== 'any') {
          queryParameters.append('visit_type', visitType);
        }
        // Update the url, triggering page reload and effective search
        window.location = `${Urls.browse_search()}?${queryParameters.toString()}`;
      } else {
        $(event.target).addClass('was-validated');
      }
    });

    $('#origins-next-results-button').on('click', event => {
      if ($('#origins-next-results-button').hasClass('disabled') || inSearch) {
        return;
      }
      inSearch = true;
      linksPrev.push(linkCurrent);
      searchOrigins(linkNext);
      event.preventDefault();
    });

    $('#origins-prev-results-button').on('click', event => {
      if ($('#origins-prev-results-button').hasClass('disabled') || inSearch) {
        return;
      }
      inSearch = true;
      searchOrigins(linksPrev.pop());
      event.preventDefault();
    });

    $('.page-item').on('keypress', e => {
      if (e.key === 'Enter') {
        $(e.target).trigger('click');
      }
    });

    if (window.location.search) {
      const urlParams = new URLSearchParams(window.location.search);
      const query = urlParams.get('q');
      const withVisit = urlParams.has('with_visit');
      const useSearchQL = urlParams.has('use_ql');
      const withContent = urlParams.has('with_content');
      const searchMetadata = urlParams.has('search_metadata');
      const visitType = urlParams.get('visit_type');

      $('#swh-origins-url-patterns').val(query);
      $('#swh-search-origins-with-visit').prop('checked', withVisit);
      $('#swh-search-use-ql').prop('checked', useSearchQL ?? false);
      $('#swh-filter-empty-visits').prop('checked', withContent);
      $('#swh-search-origin-metadata').prop('checked', searchMetadata);
      if (visitType) {
        $('#swh-search-visit-type').val(visitType);
      }
      doSearch();
    }
  });
}
