import { openWorldshaperStudioWindow } from "../shared/windowing";
import type {
  AdminAuthPayload,
  LauncherRequest,
  LauncherRequestAnalysisItem,
  LauncherRequestAnalysisRouting,
  LauncherRequestStatus,
  LauncherWindowMode,
  RecentSaveEvent,
  WorldDefaultPayload,
} from "./types";
import { DEFAULT_EDITOR_WORLD_ID_FALLBACK } from "./types";

export function readLauncherWindowMode(): LauncherWindowMode {
  if (typeof window === "undefined") {
    return "public";
  }
  const searchParams = new URLSearchParams(window.location.search);
  return searchParams.get("admin") === "requests" ? "admin" : "public";
}

export function normalizeStringList(values: string[]): string[] {
  return Array.from(new Set(
    values
      .map((entry) => String(entry || "").trim())
      .filter(Boolean),
  )).sort((left, right) => left.localeCompare(right));
}

export function appendUniqueString(values: string[], value: string): string[] {
  const normalizedValue = String(value || "").trim();
  if (!normalizedValue) {
    return normalizeStringList(values);
  }
  return normalizeStringList([...values, normalizedValue]);
}

export function removeStringValue(values: string[], value: string): string[] {
  const normalizedValue = String(value || "").trim().toLowerCase();
  return normalizeStringList(values.filter((entry) => entry.trim().toLowerCase() !== normalizedValue));
}

export function toggleStringSelection(current: string[], value: string): string[] {
  return current.includes(value)
    ? current.filter((entry) => entry !== value)
    : [...current, value].sort((left, right) => left.localeCompare(right));
}

export function normalizeSearchText(value: string): string {
  return String(value || "").replace(/\s+/g, " ").trim().toLowerCase();
}

function extractRoutingTerms(requestEntry: LauncherRequest): string[] {
  const tagTerms = requestEntry.tags
    .map((entry) => String(entry || "").trim())
    .filter(Boolean);
  if (tagTerms.length > 0) {
    return tagTerms.slice(0, 6);
  }
  const seen = new Set<string>();
  const stopWords = new Set([
    "the", "and", "for", "with", "that", "this", "from", "into", "have", "need",
    "want", "make", "more", "just", "like", "does", "dont", "cannot", "should",
    "would", "could", "about", "because", "there", "their", "they", "them", "then",
    "than", "over", "under", "your", "while", "where",
  ]);
  const matches = `${requestEntry.title} ${requestEntry.sourceText}`.match(/[A-Za-z][A-Za-z0-9/-]{2,}/g) || [];
  return matches
    .map((entry) => entry.trim())
    .filter((entry) => {
      const normalized = entry.toLowerCase();
      if (stopWords.has(normalized) || seen.has(normalized)) {
        return false;
      }
      seen.add(normalized);
      return true;
    })
    .slice(0, 6);
}

function buildRoutingSummaryFallback(requestEntry: LauncherRequest, firstItem: LauncherRequestAnalysisItem | null): string {
  const normalizedSummary = String(requestEntry.summary || "").trim();
  if (normalizedSummary && normalizedSummary !== "Awaiting parsing and categorization.") {
    return normalizedSummary;
  }
  if (firstItem?.parsedInterpretation) {
    return String(firstItem.parsedInterpretation).trim();
  }
  const normalizedSource = String(requestEntry.sourceText || "").replace(/\s+/g, " ").trim();
  if (normalizedSource) {
    return normalizedSource.length > 220
      ? `${normalizedSource.slice(0, 217).trim()}...`
      : normalizedSource;
  }
  return "No routing summary has been stored yet.";
}

function buildFallbackRouting(requestEntry: LauncherRequest): LauncherRequestAnalysisRouting {
  const firstItem = Array.isArray(requestEntry.analysis?.items) ? requestEntry.analysis.items[0] : null;
  const routedTags = Array.isArray(firstItem?.tags) && firstItem.tags.length > 0
    ? firstItem.tags
    : requestEntry.tags.length > 0
      ? requestEntry.tags
      : (requestEntry.category && requestEntry.category !== "Unsorted" ? [requestEntry.category] : []);
  const likelySystems = Array.isArray(firstItem?.affectedSystems) && firstItem.affectedSystems.length > 0
    ? firstItem.affectedSystems
    : (routedTags.length > 0 ? routedTags : []);
  const possibleDirections = Array.isArray(firstItem?.reviewOptions) && firstItem.reviewOptions.length > 0
    ? firstItem.reviewOptions
    : (requestEntry.implementationNotes.trim() ? [requestEntry.implementationNotes.trim()] : []);
  return {
    summary: buildRoutingSummaryFallback(requestEntry, firstItem),
    ambiguity: requestEntry.status === "pending" ? "medium" : "low",
    matchedTerms: extractRoutingTerms(requestEntry),
    suggestedTags: Array.isArray(routedTags) ? routedTags : [],
    suggestedSystems: likelySystems,
    suggestedModules: [],
    rationale: String(
      firstItem?.reviewRationale
      || requestEntry.implementationNotes
      || `Routing was reconstructed from the saved request title, tags, and submission text for "${requestEntry.title}".`
    ).trim(),
    possibleDirections,
    kbSections: [],
  };
}

function mergeRoutingWithFallback(
  requestEntry: LauncherRequest,
  existingRouting: LauncherRequestAnalysisRouting | undefined,
): LauncherRequestAnalysisRouting {
  const fallbackRouting = buildFallbackRouting(requestEntry);
  const normalizedRouting = existingRouting || {};
  return {
    summary: String(normalizedRouting.summary || "").trim() || fallbackRouting.summary,
    ambiguity: normalizedRouting.ambiguity || fallbackRouting.ambiguity,
    matchedTerms: Array.isArray(normalizedRouting.matchedTerms) && normalizedRouting.matchedTerms.length > 0
      ? normalizedRouting.matchedTerms
      : fallbackRouting.matchedTerms,
    suggestedTags: Array.isArray(normalizedRouting.suggestedTags) && normalizedRouting.suggestedTags.length > 0
      ? normalizedRouting.suggestedTags
      : fallbackRouting.suggestedTags,
    suggestedSystems: Array.isArray(normalizedRouting.suggestedSystems) && normalizedRouting.suggestedSystems.length > 0
      ? normalizedRouting.suggestedSystems
      : fallbackRouting.suggestedSystems,
    suggestedModules: Array.isArray(normalizedRouting.suggestedModules) && normalizedRouting.suggestedModules.length > 0
      ? normalizedRouting.suggestedModules
      : fallbackRouting.suggestedModules,
    rationale: String(normalizedRouting.rationale || "").trim() || fallbackRouting.rationale,
    possibleDirections: Array.isArray(normalizedRouting.possibleDirections) && normalizedRouting.possibleDirections.length > 0
      ? normalizedRouting.possibleDirections
      : fallbackRouting.possibleDirections,
    kbSections: Array.isArray(normalizedRouting.kbSections) && normalizedRouting.kbSections.length > 0
      ? normalizedRouting.kbSections
      : fallbackRouting.kbSections,
  };
}

export function hydrateLauncherRequestForUi(requestEntry: LauncherRequest): LauncherRequest {
  const nextRequest = cloneLauncherRequest(requestEntry);
  nextRequest.analysis = {
    ...(nextRequest.analysis || {}),
    createdAt: nextRequest.analysis?.createdAt || nextRequest.createdAt,
    updatedAt: nextRequest.analysis?.updatedAt || nextRequest.updatedAt,
    itemCount: nextRequest.analysis?.itemCount ?? (Array.isArray(nextRequest.analysis?.items) ? nextRequest.analysis?.items.length : 0),
    items: Array.isArray(nextRequest.analysis?.items) ? nextRequest.analysis.items : [],
    routing: mergeRoutingWithFallback(nextRequest, nextRequest.analysis?.routing),
  };
  return nextRequest;
}

function buildRequestSearchCorpus(requestEntry: LauncherRequest): string {
  return normalizeSearchText([
    requestEntry.title,
    requestEntry.category,
    requestEntry.tags.join(" "),
    requestEntry.sourceText,
    requestEntry.summary,
    requestEntry.implementationNotes,
    requestEntry.analysis?.routing?.summary,
    requestEntry.analysis?.routing?.rationale,
    ...(Array.isArray(requestEntry.analysis?.routing?.matchedTerms) ? requestEntry.analysis?.routing?.matchedTerms : []),
    ...(Array.isArray(requestEntry.analysis?.items)
      ? requestEntry.analysis.items.flatMap((item) => [
        item.title,
        item.primaryCategory,
        item.parsedInterpretation,
        item.implementationApproach,
        ...(Array.isArray(item.tags) ? item.tags : []),
      ])
      : []),
  ].filter(Boolean).join(" "));
}

function matchesRequestFilterToken(requestEntry: LauncherRequest, token: string): boolean {
  if (token === "pending") {
    return requestEntry.status === "pending";
  }
  if (token === "queued") {
    return isQueuedPendingRequest(requestEntry);
  }
  if (token === "review") {
    return isNeedsReviewRequest(requestEntry);
  }
  if (token === "active") {
    return requestEntry.status === "active";
  }
  if (token === "implemented") {
    return requestEntry.status === "implemented";
  }
  return false;
}

export function requestMatchesFilters(
  requestEntry: LauncherRequest,
  searchText: string,
  statusSelections: string[],
  tagSelections: string[],
): boolean {
  const normalizedSearchText = normalizeSearchText(searchText);
  if (normalizedSearchText && !buildRequestSearchCorpus(requestEntry).includes(normalizedSearchText)) {
    return false;
  }
  if (statusSelections.length > 0 && !statusSelections.some((token) => matchesRequestFilterToken(requestEntry, token))) {
    return false;
  }
  if (tagSelections.length > 0 && !tagSelections.some((tag) => requestEntry.tags.includes(tag))) {
    return false;
  }
  return true;
}

export function FilterIcon() {
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <path d="M4 6h16M7 12h10M10 18h4" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

export function LogsIcon() {
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <path d="M5 5h14v14H5z" fill="none" stroke="currentColor" strokeWidth="1.8" />
      <path d="M8 9h8M8 12h8M8 15h5" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
    </svg>
  );
}

export function SaveIcon() {
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <path d="M5 4h11l3 3v13H5z" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" />
      <path d="M8 4h7v5H8zM8 14h8v5H8z" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" />
    </svg>
  );
}

export function CheckIcon() {
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <path d="M5 12.5l4.2 4.2L19 7" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

export function PlayIcon() {
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <path d="M8 6l10 6-10 6z" fill="currentColor" />
    </svg>
  );
}

type LauncherChipSelectorProps = {
  label: string;
  values: string[];
  options: string[];
  placeholder: string;
  emptyLabel?: string;
  onAdd: (value: string) => void;
  onRemove: (value: string) => void;
};

export function LauncherChipSelector({
  label,
  values,
  options,
  placeholder,
  emptyLabel = "No tags selected yet.",
  onAdd,
  onRemove,
}: LauncherChipSelectorProps) {
  const availableOptions = options.filter((option) => !values.includes(option));
  return (
    <div className="launcher-chip-field">
      <div className="launcher-chip-field-head">
        <span className="launcher-request-filter-label">{label}</span>
        <select
          className="launcher-request-filter-select"
          value=""
          onChange={(event) => {
            const nextValue = event.target.value;
            if (!nextValue) {
              return;
            }
            onAdd(nextValue);
          }}
          disabled={availableOptions.length === 0}
        >
          <option value="">{availableOptions.length > 0 ? placeholder : "Everything added"}</option>
          {availableOptions.map((option) => (
            <option key={`${label}-${option}`} value={option}>{option}</option>
          ))}
        </select>
      </div>
      <div className="launcher-chip-list">
        {values.length > 0 ? values.map((value) => (
          <span key={`${label}-${value}`} className="launcher-chip">
            <span>{value}</span>
            <button
              type="button"
              className="launcher-chip-remove"
              onClick={() => onRemove(value)}
              aria-label={`Remove ${value}`}
              title={`Remove ${value}`}
            >
              x
            </button>
          </span>
        )) : (
          <span className="launcher-chip-empty">{emptyLabel}</span>
        )}
      </div>
    </div>
  );
}

export async function resolveDefaultWorldId(): Promise<string> {
  const response = await fetch("/api/world-default");
  if (!response.ok) {
    throw new Error(`Failed to load default world (${response.status}).`);
  }
  const payload = await response.json() as WorldDefaultPayload;
  const resolvedWorldId = String(payload.worldId || payload.world?.id || "").trim();
  return resolvedWorldId || DEFAULT_EDITOR_WORLD_ID_FALLBACK;
}

export async function fetchJsonOrThrow<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T> {
  const response = await fetch(input, init);
  if (!response.ok) {
    let detail = `Request failed (${response.status}).`;
    try {
      const payload = await response.json() as { error?: string };
      detail = String(payload?.error || detail);
    } catch {
      // Ignore JSON parse failures and fall back to status text.
    }
    throw new Error(detail);
  }
  return response.json() as Promise<T>;
}

export function buildAdminHeaders(password: string, headers?: HeadersInit): HeadersInit {
  const normalizedPassword = String(password || "").trim();
  if (!normalizedPassword) {
    return {
      ...(headers || {}),
    };
  }
  return {
    ...(headers || {}),
    "x-worldshaper-admin-password": normalizedPassword,
  };
}

export function isAdminAccessError(error: unknown): boolean {
  const text = String(error || "").toLowerCase();
  return text.includes("admin access denied")
    || text.includes("admin access is not configured");
}

export function cloneLauncherRequest(requestEntry: LauncherRequest): LauncherRequest {
  return JSON.parse(JSON.stringify(requestEntry)) as LauncherRequest;
}

export function formatRequestTimestamp(value: string): string {
  const parsed = Date.parse(String(value || ""));
  if (!Number.isFinite(parsed)) {
    return "Saved recently";
  }
  return new Intl.DateTimeFormat(undefined, {
    month: "short",
    day: "numeric",
    hour: "numeric",
    minute: "2-digit",
  }).format(parsed);
}

export function formatRequestSubmittedDate(value: string): string {
  const parsed = Date.parse(String(value || ""));
  if (!Number.isFinite(parsed)) {
    return "Recently";
  }
  return new Intl.DateTimeFormat(undefined, {
    month: "short",
    day: "numeric",
    year: "numeric",
  }).format(parsed);
}

export function normalizeAnalysisState(value: string | undefined): string {
  return String(value || "").trim().toLowerCase();
}

export function formatAnalysisStateLabel(value: string | undefined): string {
  const normalized = normalizeAnalysisState(value);
  if (normalized === "processing") {
    return "Processing";
  }
  if (normalized === "processed") {
    return "Processed";
  }
  if (normalized === "needs_review") {
    return "Needs Review";
  }
  if (normalized === "error") {
    return "Error";
  }
  return "Unprocessed";
}

export function getRequestDisplayStateLabel(requestEntry: LauncherRequest): string {
  if (requestEntry.status === "implemented") {
    return "Implemented";
  }
  if (requestEntry.status === "active") {
    return "Active";
  }
  const analysisState = normalizeAnalysisState(requestEntry.analysis?.state);
  if (!analysisState || analysisState === "unprocessed") {
    return "Queued";
  }
  if (analysisState === "needs_review") {
    return "Needs Review";
  }
  if (analysisState === "error") {
    return "Analysis Error";
  }
  if (analysisState === "processed") {
    return "Reviewed";
  }
  return formatAnalysisStateLabel(analysisState);
}

export function getRequestDisplayStateClassName(requestEntry: LauncherRequest): string {
  if (requestEntry.status === "implemented") {
    return "implemented";
  }
  if (requestEntry.status === "active") {
    return "active";
  }
  const analysisState = normalizeAnalysisState(requestEntry.analysis?.state);
  if (!analysisState || analysisState === "unprocessed") {
    return "queued";
  }
  if (analysisState === "needs_review") {
    return "needs-review";
  }
  if (analysisState === "error") {
    return "error";
  }
  if (analysisState === "processed") {
    return "processed";
  }
  if (analysisState === "processing") {
    return "processing";
  }
  return "pending";
}

export function isQueuedPendingRequest(requestEntry: LauncherRequest): boolean {
  const analysisState = normalizeAnalysisState(requestEntry.analysis?.state);
  return requestEntry.status === "pending" && (!analysisState || analysisState === "unprocessed" || analysisState === "processing");
}

export function isNeedsReviewRequest(requestEntry: LauncherRequest): boolean {
  const analysisState = normalizeAnalysisState(requestEntry.analysis?.state);
  return requestEntry.status === "pending" && (analysisState === "needs_review" || analysisState === "error");
}

export function formatEventLabel(event: RecentSaveEvent): string {
  switch (String(event.type || "").trim()) {
    case "launcher-request-add":
      return "Request submitted";
    case "launcher-request-delete":
      return "Request deleted";
    case "launcher-request-update":
      return "Request updated";
    case "launcher-request-review":
      return "Analysis saved for review";
    case "launcher-request-promote":
      return "Pending request promoted";
    case "launcher-request-analysis-error":
      return "Analysis failed";
    case "launcher-request-analysis-launch":
      return "Queue worker launched";
    case "launcher-request-analysis-finish":
      return "Queue worker finished";
    case "launcher-request-analysis-launch-error":
      return "Queue worker launch error";
    case "launcher-request-analysis-requeue":
      return "Request requeued for review";
    default:
      return String(event.type || "Event");
  }
}

export function formatEventDetail(event: RecentSaveEvent): string {
  const parts = [
    event.requestId ? `Request ${event.requestId}` : "",
    event.category ? `Category ${event.category}` : "",
    event.status ? `Status ${event.status}` : "",
    event.itemCount ? `${event.itemCount} item${event.itemCount === 1 ? "" : "s"}` : "",
    event.provider ? `Provider ${event.provider}` : "",
    event.model ? `Model ${event.model}` : "",
    event.reason ? `Reason ${event.reason}` : "",
    event.pid ? `PID ${event.pid}` : "",
    Number.isFinite(Number(event.code)) ? `Exit ${event.code}` : "",
    event.signal ? `Signal ${event.signal}` : "",
    event.error ? String(event.error) : "",
    event.textPreview ? `Preview: ${event.textPreview}` : "",
  ].filter(Boolean);
  return parts.join(" | ");
}

export function openStudioPopup(worldId: string): boolean {
  const popup = openWorldshaperStudioWindow(worldId, window, { worldId });
  return Boolean(popup);
}

export function openRepo(): void {
  window.location.assign("https://repo.andraxion.net/");
}

export function openAdminPanelWindow(): boolean {
  const nextUrl = new URL(window.location.href);
  nextUrl.searchParams.set("admin", "requests");
  nextUrl.searchParams.set("tab", "requests");
  const popup = window.open(nextUrl.toString(), "worldshaper-admin-panel", "popup=yes,width=1620,height=980,resizable=yes,scrollbars=yes");
  if (popup) {
    popup.focus();
  }
  return Boolean(popup);
}

export type { AdminAuthPayload, LauncherRequestStatus };
