import { resolveFamilyKind } from '@ema-platform/api';
import type { LicenseStatus, QueueCounts, QueueFilter } from '@ema-platform/api';

export type SavedViewId =
  | 'unassigned'
  | 'mine'
  | 'awaitingApplicant'
  | 'overdue'
  | 'readyToIssue'
  | 'all';

export interface SavedView {
  id: SavedViewId;
  labelKey: string;
  /** Which count from `/counts` labels the tab. */
  countKey: keyof QueueCounts;
  /** Filters this view pins. The user's own facets layer on top. */
  filter: Partial<QueueFilter>;
  /** Which list endpoint backs it. */
  source: 'queue' | 'mine' | 'all';
}

const AWAITING_APPLICANT: LicenseStatus[] = ['RESUBMIT_REQUIRED'];
const READY_TO_ISSUE: LicenseStatus[] = ['PAYMENT_CONFIRMED'];

/**
 * The officer's saved views.
 *
 * Replaces a two-option SegmentedControl (Unclaimed / Mine) that could not
 * express the questions officers actually ask — what is late, what is waiting
 * on the applicant, what is ready to issue. Each is a filter preset over the
 * same grid rather than a separate screen.
 */
export const SAVED_VIEWS: SavedView[] = [
  {
    id: 'unassigned',
    labelKey: 'queue.views.unassigned',
    countKey: 'unassigned',
    filter: {},
    source: 'queue',
  },
  {
    id: 'mine',
    labelKey: 'queue.views.mine',
    countKey: 'mine',
    filter: {},
    source: 'mine',
  },
  {
    id: 'awaitingApplicant',
    labelKey: 'queue.views.awaitingApplicant',
    countKey: 'awaitingApplicant',
    filter: { status: AWAITING_APPLICANT },
    source: 'all',
  },
  {
    id: 'overdue',
    labelKey: 'queue.views.overdue',
    countKey: 'overdue',
    filter: { overdue: true },
    source: 'all',
  },
  {
    id: 'readyToIssue',
    labelKey: 'queue.views.readyToIssue',
    countKey: 'readyToIssue',
    filter: { status: READY_TO_ISSUE },
    source: 'all',
  },
  {
    id: 'all',
    labelKey: 'queue.views.all',
    countKey: 'all',
    filter: {},
    source: 'all',
  },
];

export const DEFAULT_VIEW: SavedViewId = 'unassigned';

/**
 * Type keys reviewed off a shared unclaimed pool despite not being logistics
 * licences. Whether a queue is claimed is an officer-workflow property, not a
 * label one: vessel registrations arrive unassigned and officers claim them,
 * even though the family kind is DOCUMENT because the certificate they produce
 * is a document rather than a licence.
 */
const CLAIMABLE_NON_LOGISTICS = new Set([
  'VESSEL_REGISTRATION',
  // Endorsements run the standard claim-first workflow: applications arrive
  // unassigned and an officer claims them, even though the family kind is
  // CERTIFICATE.
  'ENDORSEMENT_SEAFARER',
]);

/**
 * Does this queue have an unclaimed pool to triage?
 *
 * True for the mixed All/Mine grids (no type pinned) — nothing is hidden when
 * the queue spans every type. False for the person-centric services (Seafarer
 * Registration, Seaman Book, BTC, CoC, ...), whose applications aren't claimed
 * off a shared queue, so the tab and the claim actions don't apply there.
 */
export function hasUnclaimedPool(typeCode: string | undefined): boolean {
  if (!typeCode) return true;
  return (
    resolveFamilyKind(typeCode) === 'LOGISTICS_LICENSE' ||
    CLAIMABLE_NON_LOGISTICS.has(typeCode)
  );
}

/** Drops the Unassigned tab on queues with no unclaimed pool. */
export function savedViewsForFamily(claimable: boolean): SavedView[] {
  return claimable
    ? SAVED_VIEWS
    : SAVED_VIEWS.filter((v) => v.id !== 'unassigned');
}

const LAST_VIEW_KEY = 'ema-backoffice-queue-view';

export function readLastView(): SavedViewId {
  try {
    const stored = localStorage.getItem(LAST_VIEW_KEY) as SavedViewId | null;
    return SAVED_VIEWS.some((v) => v.id === stored) ? (stored as SavedViewId) : DEFAULT_VIEW;
  } catch {
    return DEFAULT_VIEW;
  }
}

export function writeLastView(id: SavedViewId): void {
  try {
    localStorage.setItem(LAST_VIEW_KEY, id);
  } catch {
    // Not persisting the last view is cosmetic; never break the page for it.
  }
}

// ------------------------------------------------------------ URL round-trip

/**
 * Reads the user's facets out of the query string.
 *
 * Filters live in the URL so a filtered queue is a shareable link — "here are
 * the six overdue MTO applications" should be something an officer can paste
 * into a message, not a state they describe in prose.
 */
export function filterFromSearchParams(params: URLSearchParams): QueueFilter {
  const filter: QueueFilter = {};
  const search = params.get('q');
  if (search) filter.search = search;
  const type = params.get('type');
  if (type) filter.licenseTypeId = type;
  const status = params.get('status');
  if (status) filter.status = status.split(',') as LicenseStatus[];
  const assignee = params.get('assignee');
  if (assignee) filter.assignee = assignee;
  const from = params.get('from');
  if (from) filter.submittedFrom = from;
  const to = params.get('to');
  if (to) filter.submittedTo = to;
  const sortBy = params.get('sort');
  if (sortBy) filter.sortBy = sortBy as QueueFilter['sortBy'];
  const sortDir = params.get('dir');
  if (sortDir === 'ASC' || sortDir === 'DESC') filter.sortDir = sortDir;
  const page = params.get('page');
  if (page) {
    const parsed = Number(page);
    if (Number.isFinite(parsed) && parsed > 0) filter.skip = undefined;
  }
  return filter;
}

/** Inverse of {@link filterFromSearchParams}. Omits defaults to keep URLs short. */
export function searchParamsFromFilter(
  filter: QueueFilter,
  view: SavedViewId,
  page: number,
): URLSearchParams {
  const params = new URLSearchParams();
  if (view !== DEFAULT_VIEW) params.set('view', view);
  if (filter.search) params.set('q', filter.search);
  if (filter.licenseTypeId) params.set('type', filter.licenseTypeId);
  if (filter.status?.length) params.set('status', filter.status.join(','));
  if (filter.assignee) params.set('assignee', filter.assignee);
  if (filter.submittedFrom) params.set('from', filter.submittedFrom);
  if (filter.submittedTo) params.set('to', filter.submittedTo);
  if (filter.sortBy) params.set('sort', filter.sortBy);
  if (filter.sortDir && filter.sortDir !== 'ASC') params.set('dir', filter.sortDir);
  if (page > 1) params.set('page', String(page));
  return params;
}
