"use client";

import { useState, useEffect, useCallback, useRef, Fragment } from "react";
import type { ConsentGrantMeta, UserConsentState } from "@repo/core-consent";
import { useConsent, ConsentContextError } from "@repo/core-consent/react";
import { cn } from "../lib/utils";
import { readConsentCookie, writeConsentCookie } from "./cookie-helpers";
import type {
  CookieCategory,
  CookieConsentBannerProps,
  HeaderRenderProps,
  CategoryRowRenderProps,
  ActionsRenderProps,
} from "./types";

export const DEFAULT_CATEGORIES: CookieCategory[] = [
  {
    id: "essential",
    label: "Essential",
    description:
      "Required for the website to function correctly. Cannot be disabled.",
    required: true,
  },
  {
    id: "functional",
    label: "Functional",
    description: "Enable enhanced functionality and personalisation.",
    required: false,
  },
  {
    id: "analytics",
    label: "Analytics",
    description: "Help us understand how visitors interact with our website.",
    required: false,
  },
  {
    id: "marketing",
    label: "Marketing",
    description: "Used to deliver personalised advertisements.",
    required: false,
  },
];

const BANNER_TITLE = "Cookie Preferences";
const FOCUSABLE_SELECTOR =
  'button:not(:disabled), [href], input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"])';

function useOptionalConsent() {
  try {
    return useConsent();
  } catch (e) {
    if (e instanceof ConsentContextError) return null;
    throw e;
  }
}

function buildInitialStates(
  categories: CookieCategory[],
  consent: ReturnType<typeof useOptionalConsent>,
): Record<string, boolean> {
  const cookie = readConsentCookie();
  if (cookie) {
    return Object.fromEntries(
      categories.map((c) => [c.id, cookie.categories[c.id] ?? c.required]),
    );
  }
  if (consent) {
    return Object.fromEntries(
      categories.map((c) => [c.id, c.required || consent.isGranted(c.id)]),
    );
  }
  return Object.fromEntries(categories.map((c) => [c.id, c.required]));
}

function toConsentStates(
  categories: CookieCategory[],
  states: Record<string, boolean>,
): UserConsentState[] {
  return categories.map((cat) => ({
    category: cat.id,
    state: (states[cat.id] ? "granted" : "denied") as UserConsentState["state"],
    method: "banner",
  }));
}

function DefaultHeader({ title }: HeaderRenderProps) {
  return (
    <h2
      id="cookie-consent-title"
      className="text-lg font-semibold text-foreground"
    >
      {title}
    </h2>
  );
}

function DefaultCategoryRow({
  category,
  enabled,
  onToggle,
}: CategoryRowRenderProps) {
  return (
    <div className="flex items-start justify-between gap-4 py-3 border-b border-border last:border-0">
      <div className="flex-1 min-w-0">
        <p className="font-medium text-sm text-foreground">{category.label}</p>
        <p className="text-sm text-muted-foreground">{category.description}</p>
      </div>
      <button
        role="switch"
        aria-checked={enabled}
        aria-label={`${category.label} cookies`}
        disabled={category.required}
        onClick={() => onToggle(category.id, !enabled)}
        className={cn(
          "relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent",
          "transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
          "disabled:cursor-not-allowed disabled:opacity-50",
          enabled ? "bg-primary" : "bg-input",
        )}
      >
        <span
          className={cn(
            "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform",
            enabled ? "translate-x-5" : "translate-x-0",
          )}
        />
      </button>
    </div>
  );
}

const btnBase =
  "flex-1 inline-flex items-center justify-center rounded-md font-medium transition-colors h-10 px-4 py-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";

function DefaultActions({
  onRejectAll,
  onAcceptAll,
  onSaveSelected,
}: ActionsRenderProps) {
  return (
    <div className="flex flex-col gap-2 pt-4">
      <div className="flex gap-2">
        {/* Reject All precedes Accept All in DOM order — CNIL equal-prominence + tab order */}
        <button
          onClick={onRejectAll}
          className={cn(
            btnBase,
            "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
          )}
        >
          Reject All
        </button>
        <button
          onClick={onAcceptAll}
          className={cn(
            btnBase,
            "bg-primary text-primary-foreground hover:bg-primary/90",
          )}
        >
          Accept All
        </button>
      </div>
      <button
        onClick={onSaveSelected}
        className="w-full inline-flex items-center justify-center rounded-md font-medium transition-colors h-10 px-4 py-2 bg-secondary text-secondary-foreground hover:bg-secondary/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
      >
        Save Selected
      </button>
    </div>
  );
}

export function CookieConsentBanner({
  variant,
  categories = DEFAULT_CATEGORIES,
  onConsentChange,
  renderHeader,
  renderCategoryRow,
  renderActions,
}: CookieConsentBannerProps) {
  const consent = useOptionalConsent();
  const dialogRef = useRef<HTMLDivElement>(null);

  const [categoryStates, setCategoryStates] = useState<Record<string, boolean>>(
    () => buildInitialStates(categories, consent),
  );

  const applyConsent = useCallback(
    async (states: Record<string, boolean>) => {
      writeConsentCookie({ _v: 1, categories: states });

      if (consent) {
        for (const [id, granted] of Object.entries(states)) {
          const meta: ConsentGrantMeta = { method: "banner-accept" };
          if (granted) {
            await consent.grant(id, meta);
          } else {
            await consent.withdraw(id);
          }
        }
      }

      onConsentChange?.(toConsentStates(categories, states));
    },
    [consent, onConsentChange, categories],
  );

  const handleRejectAll = useCallback(async () => {
    const next = Object.fromEntries(categories.map((c) => [c.id, c.required]));
    setCategoryStates(next);
    await applyConsent(next);
  }, [categories, applyConsent]);

  const handleAcceptAll = useCallback(async () => {
    const next = Object.fromEntries(categories.map((c) => [c.id, true]));
    setCategoryStates(next);
    await applyConsent(next);
  }, [categories, applyConsent]);

  const handleSaveSelected = useCallback(async () => {
    await applyConsent(categoryStates);
  }, [categoryStates, applyConsent]);

  const handleToggle = useCallback((id: string, enabled: boolean) => {
    setCategoryStates((prev) => ({ ...prev, [id]: enabled }));
  }, []);

  // ESC = Reject All for modal variant (explicit legal choice, not silent dismiss)
  useEffect(() => {
    if (variant !== "modal") return;
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape") void handleRejectAll();
    };
    document.addEventListener("keydown", onKeyDown);
    return () => document.removeEventListener("keydown", onKeyDown);
  }, [variant, handleRejectAll]);

  // Focus trap for modal
  useEffect(() => {
    if (variant !== "modal") return;
    const container = dialogRef.current;
    if (!container) return;

    const getFocusable = () =>
      Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));

    getFocusable()[0]?.focus();

    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key !== "Tab") return;
      const elements = getFocusable();
      if (elements.length === 0) return;
      const first = elements[0];
      const last = elements[elements.length - 1];
      if (!first || !last) return;

      if (e.shiftKey) {
        if (document.activeElement === first) {
          e.preventDefault();
          last.focus();
        }
      } else {
        if (document.activeElement === last) {
          e.preventDefault();
          first.focus();
        }
      }
    };

    document.addEventListener("keydown", onKeyDown);
    return () => document.removeEventListener("keydown", onKeyDown);
  }, [variant]);

  const headerNode = (renderHeader ?? DefaultHeader)({ title: BANNER_TITLE });
  const actionsNode = (renderActions ?? DefaultActions)({
    onRejectAll: handleRejectAll,
    onAcceptAll: handleAcceptAll,
    onSaveSelected: handleSaveSelected,
  });

  const content = (
    <>
      {headerNode}
      <div className="mt-4 space-y-1">
        {categories.map((cat) => (
          <Fragment key={cat.id}>
            {(renderCategoryRow ?? DefaultCategoryRow)({
              category: cat,
              enabled: categoryStates[cat.id] ?? cat.required,
              onToggle: handleToggle,
            })}
          </Fragment>
        ))}
      </div>
      {actionsNode}
    </>
  );

  if (variant === "modal") {
    return (
      <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
        <div
          ref={dialogRef}
          role="dialog"
          aria-modal="true"
          aria-labelledby="cookie-consent-title"
          className="relative w-full max-w-lg rounded-lg bg-background p-6 shadow-lg"
        >
          {content}
        </div>
      </div>
    );
  }

  return (
    <div
      role="region"
      aria-label={BANNER_TITLE}
      className="fixed bottom-0 left-0 right-0 z-50 bg-background border-t border-border p-6 shadow-lg"
    >
      <div className="mx-auto max-w-4xl">{content}</div>
    </div>
  );
}
