(function (root, factory) {
  const api = factory();
  if (typeof module === 'object' && module.exports) module.exports = api;
  else root.createTaskOverlayHistory = api;
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
  'use strict';

  const allowed = new Set(['new', 'find', 'search', 'search-preview', 'plan-today', 'plan-today-preview', 'today-readiness']);
  const searchKinds = new Set(['search', 'search-preview']);
  const maxQueryLength = 200;

  function cleanQuery(value) {
    const query = typeof value === 'string' ? value.trim() : '';
    return query.length <= maxQueryLength ? query : '';
  }

  function cleanScope(value) {
    const repository = typeof value?.repository === 'string' &&
      /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(value.repository.trim())
      ? value.repository.trim() : '';
    return {
      kind:['all', 'issue', 'pull'].includes(value?.kind) ? value.kind : 'all',
      state:['all', 'open', 'closed'].includes(value?.state) ? value.state : 'all',
      repository,
    };
  }

  function cleanPreview(value) {
    if (!value || !['issue', 'pull'].includes(value.kind)) return null;
    const repository = typeof value.repository === 'string' ? value.repository : '';
    const parts = repository.split('/');
    const validPart = part => /^[A-Za-z0-9_.-]+$/.test(part);
    const number = Number(value.number);
    if (parts.length !== 2 || !parts.every(validPart) || !Number.isInteger(number) || number < 1) return null;
    return { kind:value.kind, repository, number };
  }

  function previewToken(preview) {
    return preview ? `${preview.kind}:${preview.repository}:${preview.number}` : '';
  }

  function parsePreview(value) {
    const match = /^(issue|pull):([^/:]+\/[^/:]+):(\d+)$/.exec(value || '');
    return match ? cleanPreview({ kind:match[1], repository:match[2], number:Number(match[3]) }) : null;
  }

  return function createTaskOverlayHistory({ history, location, eventTarget, onChange }) {
    let started = false;
    let restoredFromUrl = false;

    function stateDetail(state = history.state) {
      const kind = allowed.has(state?.taskOverlay) ? state.taskOverlay : null;
      if (!searchKinds.has(kind)) return { kind };
      const query = cleanQuery(state.searchQuery);
      const scope = state.searchScope === undefined ? null : cleanScope(state.searchScope);
      const preview = kind === 'search-preview' ? cleanPreview(state.searchPreview) : null;
      if (kind === 'search-preview' && state.searchPreview !== undefined && !preview) {
        return { kind:'search', ...(query ? { query } : {}) };
      }
      return { kind, ...(query ? { query } : {}), ...(scope ? { scope } : {}), ...(preview ? { preview } : {}) };
    }

    function urlDetail() {
      if (!location || typeof URLSearchParams === 'undefined') return { kind:null };
      const params = new URLSearchParams(location.search || '');
      const rawQuery = params.get('search');
      if (rawQuery === null) return { kind:null };
      const query = cleanQuery(rawQuery);
      if (rawQuery.trim() && !query) return { kind:null };
      const preview = parsePreview(params.get('preview'));
      const hasScope = params.has('search_kind') || params.has('search_state') || params.has('search_repository');
      const scope = hasScope ? cleanScope({
        kind:params.get('search_kind'), state:params.get('search_state'),
        repository:params.get('search_repository'),
      }) : null;
      return {
        kind:preview ? 'search-preview' : 'search',
        ...(query ? { query } : {}),
        ...(scope ? { scope } : {}),
        ...(preview ? { preview } : {}),
      };
    }

    function toState(detail, base = history.state) {
      const state = { ...(base || {}), taskOverlay:detail.kind };
      delete state.searchQuery;
      delete state.searchPreview;
      delete state.searchScope;
      if (searchKinds.has(detail.kind)) {
        if (detail.query) state.searchQuery = detail.query;
        if (detail.scope) state.searchScope = cleanScope(detail.scope);
        if (detail.kind === 'search-preview' && detail.preview) state.searchPreview = detail.preview;
      }
      return state;
    }

    function urlFor(detail) {
      if (!location || typeof URLSearchParams === 'undefined') return undefined;
      const params = new URLSearchParams(location.search || '');
      params.delete('search');
      params.delete('preview');
      params.delete('search_kind');
      params.delete('search_state');
      params.delete('search_repository');
      if (searchKinds.has(detail.kind)) {
        params.set('search', detail.query || '');
        if (detail.scope) {
          params.set('search_kind', detail.scope.kind);
          params.set('search_state', detail.scope.state);
          if (detail.scope.repository) params.set('search_repository', detail.scope.repository);
        }
        if (detail.kind === 'search-preview' && detail.preview) params.set('preview', previewToken(detail.preview));
      }
      const query = params.toString();
      return `${location.pathname || ''}${query ? `?${query}` : ''}${location.hash || ''}`;
    }

    let initial = stateDetail();
    if (!initial.kind) {
      const fromUrl = urlDetail();
      if (fromUrl.kind) {
        initial = fromUrl;
        restoredFromUrl = true;
        history.replaceState(toState(fromUrl), '', urlFor(fromUrl));
      }
    }
    let active = initial;

    function apply(state) {
      const next = stateDetail(state);
      if (JSON.stringify(next) === JSON.stringify(active)) return;
      const previous = active.kind;
      active = next;
      onChange(next.kind, previous, next);
    }

    return {
      start() {
        if (started) return;
        started = true;
        eventTarget.addEventListener('popstate', event => apply(event.state));
        if (restoredFromUrl) onChange(active.kind, null, active);
      },
      open(kind, detail = {}) {
        if (!allowed.has(kind)) return false;
        const next = stateDetail(toState({
          kind, query:cleanQuery(detail.query),
          scope:detail.scope === undefined ? undefined : cleanScope(detail.scope),
          preview:cleanPreview(detail.preview),
        }));
        if (active.kind === next.kind && JSON.stringify(active) === JSON.stringify(next)) return true;
        const previous = active.kind;
        history.pushState(toState(next), '', urlFor(next));
        active = next;
        onChange(next.kind, previous, next);
        return true;
      },
      update(detail = {}) {
        if (!searchKinds.has(active.kind)) return false;
        const next = stateDetail(toState({
          kind:active.kind,
          query:detail.query === undefined ? active.query : cleanQuery(detail.query),
          scope:detail.scope === undefined ? active.scope : cleanScope(detail.scope),
          preview:detail.preview === undefined ? active.preview : cleanPreview(detail.preview),
        }));
        history.replaceState(toState(next), '', urlFor(next));
        active = next;
        return true;
      },
      close() {
        if (!active.kind) return false;
        history.back();
        return true;
      },
      leave() {
        if (!active.kind) return false;
        const previous = active.kind;
        const state = { ...(history.state || {}) };
        delete state.taskOverlay;
        delete state.searchQuery;
        delete state.searchPreview;
        delete state.searchScope;
        active = { kind:null };
        history.replaceState(state, '', urlFor(active));
        onChange(null, previous, active);
        return true;
      },
      current() { return active.kind; },
      currentState() { return { ...active, ...(active.preview ? { preview:{ ...active.preview } } : {}) }; },
    };
  };
});
