import type { HttpRequestHeader } from "@yaakapp-internal/models";
import { HStack } from "@yaakapp-internal/ui";
import { acceptLanguages } from "../lib/data/acceptLanguages";
import { cacheControlDirectives } from "../lib/data/cacheControl";
import { charsets } from "../lib/data/charsets";
import { connections } from "../lib/data/connections";
import { encodings } from "../lib/data/encodings";
import type { HeaderValuePreset } from "../lib/data/headerValuePresets";
import { headerNames } from "../lib/data/headerNames";
import { mimeTypes } from "../lib/data/mimetypes";
import { userAgents } from "../lib/data/userAgents";
import { CountBadge } from "./core/CountBadge";
import { DetailsBanner } from "./core/DetailsBanner";
import type { GenericCompletion, GenericCompletionConfig } from "./core/Editor/genericCompletion";
import type { InputProps } from "./core/Input";
import type { Pair, PairEditorProps } from "./core/PairEditor";
import { PairEditorRow } from "./core/PairEditor";
import { ensurePairId } from "./core/PairEditor.util";
import { PairOrBulkEditor } from "./core/PairOrBulkEditor";

type Props = {
  forceUpdateKey: string;
  headers: HttpRequestHeader[];
  inheritedHeaders?: HttpRequestHeader[];
  inheritedHeadersLabel?: string;
  stateKey: string;
  onChange: (headers: HttpRequestHeader[]) => void;
  label?: string;
};

export function HeadersEditor({
  stateKey,
  headers,
  inheritedHeaders,
  inheritedHeadersLabel = "Inherited",
  onChange,
  forceUpdateKey,
}: Props) {
  // Get header names defined at current level (case-insensitive)
  const currentHeaderNames = new Set(
    headers.filter((h) => h.name).map((h) => h.name.toLowerCase()),
  );
  // Filter inherited headers: must be enabled, have content, and not be overridden by current level
  const validInheritedHeaders =
    inheritedHeaders?.filter(
      (pair) =>
        pair.enabled &&
        (pair.name || pair.value) &&
        !currentHeaderNames.has(pair.name.toLowerCase()),
    ) ?? [];
  const hasInheritedHeaders = validInheritedHeaders.length > 0;
  return (
    <div
      className={
        hasInheritedHeaders
          ? "@container w-full h-full grid grid-rows-[auto_minmax(0,1fr)] gap-y-1.5"
          : "@container w-full h-full"
      }
    >
      {hasInheritedHeaders && (
        <DetailsBanner
          color="secondary"
          className="text-sm"
          summary={
            <HStack>
              {inheritedHeadersLabel} <CountBadge count={validInheritedHeaders.length} />
            </HStack>
          }
        >
          <div className="pb-2">
            {validInheritedHeaders?.map((pair, i) => (
              <PairEditorRow
                key={`${pair.id}.${i}`}
                index={i}
                disabled
                disableDrag
                className="py-1"
                pair={ensurePairId(pair)}
                stateKey={null}
                nameAutocompleteFunctions
                nameAutocompleteVariables
                valueAutocompleteFunctions
                valueAutocompleteVariables
              />
            ))}
          </div>
        </DetailsBanner>
      )}
      <PairOrBulkEditor
        forceUpdateKey={forceUpdateKey}
        nameAutocomplete={nameAutocomplete}
        nameAutocompleteFunctions
        nameAutocompleteVariables
        namePlaceholder="Header-Name"
        nameValidate={validateHttpHeader}
        onChange={onChange}
        pairs={headers}
        preferenceName="headers"
        stateKey={stateKey}
        valueType={valueType}
        valueAutocomplete={valueAutocomplete}
        valueAutocompleteFunctions
        valueAutocompleteVariables
      />
    </div>
  );
}

const MIN_MATCH = 3;

const headerOptionsMap: Record<string, HeaderValuePreset[]> = {
  "content-type": mimeTypes,
  accept: ["*/*", ...mimeTypes],
  "accept-encoding": encodings,
  "content-encoding": encodings,
  connection: connections,
  "accept-charset": charsets,
  "accept-language": acceptLanguages,
  "cache-control": cacheControlDirectives,
  "user-agent": userAgents,
  pragma: ["no-cache"],
  te: ["trailers", "compress", "deflate", "gzip"],
  dnt: ["1", "0"],
  "upgrade-insecure-requests": ["1"],
  "x-requested-with": ["XMLHttpRequest"],
};

const valueType = (pair: Pair): InputProps["type"] => {
  const name = pair.name.toLowerCase().trim();
  if (
    name.includes("authorization") ||
    name.includes("api-key") ||
    name.includes("access-token") ||
    name.includes("auth") ||
    name.includes("secret") ||
    name.includes("token") ||
    name === "cookie" ||
    name === "set-cookie"
  ) {
    return "password";
  }
  return "text";
};

const valueAutocomplete = (headerName: string): GenericCompletionConfig | undefined => {
  const name = headerName.toLowerCase().trim();
  const options: GenericCompletion[] =
    headerOptionsMap[name]?.map((o) =>
      typeof o === "string"
        ? {
            label: o,
            type: "constant",
            boost: 1, // Put above other completions
          }
        : {
            // Show the short label but insert the full value (e.g. User-Agent)
            label: o.label,
            apply: o.value,
            type: "constant",
            boost: 1,
          },
    ) ?? [];
  return { minMatch: MIN_MATCH, options };
};

const nameAutocomplete: PairEditorProps["nameAutocomplete"] = {
  minMatch: MIN_MATCH,
  options: headerNames.map((t) =>
    typeof t === "string"
      ? {
          label: t,
          type: "constant",
          boost: 1, // Put above other completions
        }
      : {
          ...t,
          boost: 1, // Put above other completions
        },
  ),
};

const validateHttpHeader = (v: string) => {
  if (v === "") {
    return true;
  }

  // Template strings are not allowed so we replace them with a valid example string
  const withoutTemplateStrings = v.replace(/\$\{\[\s*[^\]\s]+\s*]}/gi, "123");
  return withoutTemplateStrings.match(/^[a-zA-Z0-9-_]+$/) !== null;
};
