<template>
  <component
    :is="tag"
    v-if="isVisible"
    v-bind="componentAttrs"
    :aria-label="computedAriaLabel"
    :disabled="isNativeButton ? disabled : undefined"
    class="inline-flex items-center justify-center gap-2 font-bold transition-all duration-200 focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-30"
    :class="[
      sizeClasses,
      variantClasses,
      radiusClasses,
      widthClasses,
      extraClass,
    ]"
  >
    <span
      v-if="hasIcon && iconPosition === 'left'"
      aria-hidden="true"
      class="inline-flex shrink-0"
    >
      <component :is="resolvedIcon" :class="iconSizeClasses" />
    </span>

    <span v-if="iconPosition !== 'center'">
      <slot>{{ label }}</slot>
    </span>

    <span v-else class="sr-only">
      <slot>{{ label }}</slot>
    </span>

    <span
      v-if="hasIcon && iconPosition === 'right'"
      aria-hidden="true"
      class="inline-flex shrink-0"
    >
      <component :is="resolvedIcon" :class="iconSizeClasses" />
    </span>

    <span
      v-if="hasIcon && iconPosition === 'center'"
      aria-hidden="true"
      class="inline-flex shrink-0"
    >
      <component :is="resolvedIcon" :class="iconSizeClasses" />
    </span>
  </component>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { useIcons } from "@/composables/useIcons";
import { useLocale } from "@/composables/useLocale";

const { path } = useLocale();
const icons = useIcons();
const { t, te } = useI18n();

type IconName =
  | "mail"
  | "document"
  | "code"
  | "external"
  | "arrowRight"
  | "arrowUp"
  | "none";

type IconPosition = "left" | "right" | "center";

type ButtonVariant =
  | "default"
  | "highlight"
  | "dark"
  | "white"
  | "pink"
  | "outlined-white"
  | "outlined-dark"
  | "outlined-pink"
  | "outlined-highlight"
  | "ghost-highlight"
  | "ghost-pink";

type ButtonSize = "xs" | "sm" | "md" | "lg";
type ButtonRadius = "none" | "sm" | "md" | "lg" | "xl" | "2xl" | "full";

interface ButtonIcon {
  name?: IconName;
  position?: IconPosition;
}

export type ButtonConfig = {
  label: string;
  url?: string;
  external?: boolean;
  enabled?: boolean;
  color?: ButtonVariant | null;
  icon?: ButtonIcon;
};

const props = withDefaults(
  defineProps<{
    button?: ButtonConfig;

    label?: string;
    href?: string;
    external?: boolean;
    disabled?: boolean;
    type?: "button" | "submit" | "reset";

    variant?: ButtonVariant;
    size?: ButtonSize;
    radius?: ButtonRadius;
    fullWidth?: boolean;
    icon?: IconName;
    iconPosition?: IconPosition;

    ariaLabel?: string;
    extraClass?: string;
  }>(),
  {
    button: undefined,
    label: "",
    href: "",
    external: false,
    disabled: false,
    type: "button",
    variant: "default",
    size: "md",
    radius: "none",
    fullWidth: false,
    icon: "none",
    iconPosition: "left",
    ariaLabel: "",
    extraClass: "",
  },
);

const isVisible = computed(() => props.button?.enabled ?? true);

const label = computed(() => props.button?.label ?? props.label);

const href = computed(() => {
  const rawUrl = props.button?.url ?? props.href;

  if (!rawUrl) return "";

  const isExternal = props.button?.external ?? props.external;

  if (isExternal) return rawUrl;

  return `${path}${rawUrl}`.replace(/\/{2,}/g, "/");
});

const isLink = computed(() => !!href.value);
const isNativeButton = computed(() => !isLink.value);
const tag = computed(() => (isLink.value ? "a" : "button"));

const componentAttrs = computed(() => {
  if (isLink.value) {
    return {
      href: href.value,
      target: (props.button?.external ?? props.external) ? "_blank" : "_self",
      rel:
        (props.button?.external ?? props.external)
          ? "noopener noreferrer"
          : undefined,
    };
  }

  return {
    type: props.type,
  };
});

const variant = computed(() => props.button?.color ?? props.variant);

const variantClasses = computed(() => {
  switch (variant.value) {
    case "highlight":
      return "bg-highlight text-white hover:bg-softlight focus-visible:ring-[#f39c12]/50";

    case "dark":
      return "bg-black text-white hover:bg-black/80 focus-visible:ring-black/40";

    case "white":
      return "bg-white text-[#d2024e] hover:bg-white/85 focus-visible:ring-white/40";

    case "pink":
      return "bg-[#d2024e] text-white hover:bg-[#d2024e]/80 focus-visible:ring-[#d2024e]/40";

    case "outlined-white":
      return "border border-white text-white hover:bg-white hover:text-black focus-visible:ring-white/40";

    case "outlined-dark":
      return "border border-black text-black hover:bg-black hover:text-white focus-visible:ring-black/40";

    case "outlined-pink":
      return "border border-[#d2024e] text-[#d2024e] hover:bg-[#d2024e] hover:text-white focus-visible:ring-[#d2024e]/40";

    case "outlined-highlight":
      return "border border-highlight text-highlight hover:bg-highlight hover:text-white focus-visible:ring-[#f39c12]/40";

    case "ghost-highlight":
      return "text-highlight hover:bg-highlight/10 focus-visible:ring-[#f39c12]/30";

    case "ghost-pink":
      return "text-[#d2024e] hover:bg-[#d2024e]/10 focus-visible:ring-[#d2024e]/30";

    default:
      return "border border-highlight/40 text-highlight hover:bg-highlight/10 focus-visible:ring-[#f39c12]/30";
  }
});

const sizeClasses = computed(() => {
  switch (props.size) {
    case "xs":
      return "px-3 py-2 text-xs";

    case "sm":
      return "px-3.5 py-2.5 text-sm";

    case "lg":
      return "px-8 py-3 text-base md:text-lg";

    default:
      return "px-5 py-3 text-sm";
  }
});

const radiusClasses = computed(() => {
  switch (props.radius) {
    case "none":
      return "rounded-none";
    case "sm":
      return "rounded-sm";
    case "md":
      return "rounded-md";
    case "lg":
      return "rounded-lg";
    case "xl":
      return "rounded-xl";
    case "2xl":
      return "rounded-2xl";
    case "full":
      return "rounded-full";
    default:
      return "rounded-none";
  }
});

const widthClasses = computed(() => {
  return props.fullWidth ? "w-full" : "";
});

const iconName = computed(() => props.button?.icon?.name ?? props.icon);
const iconPosition = computed(
  () => props.button?.icon?.position ?? props.iconPosition,
);

const resolvedIcon = computed(() => {
  if (!iconName.value || iconName.value === "none") return null;
  return icons[iconName.value as keyof typeof icons] ?? null;
});

const hasIcon = computed(() => !!resolvedIcon.value);

const iconSizeClasses = computed(() => {
  switch (props.size) {
    case "xs":
      return "size-3.5";
    case "lg":
      return "size-5";
    default:
      return "size-4";
  }
});

const computedAriaLabel = computed(() => {
  if (props.ariaLabel) return props.ariaLabel;

  const base = label.value || "";

  const external = props.button?.external ?? props.external;

  if (external) {
    const suffix = te("opensInNewTab") ? t("opensInNewTab") : "";
    return suffix ? `${base} (${suffix})` : base;
  }

  return base;
});
</script>
