import { useState, type ComponentProps } from "react";
import { AddressInputBanner } from "./index";

import { Button } from "@shared/contentful/blocks/button";
import { DocsPage } from "@shared/stories/DocsTemplate";
import type { Meta, StoryObj } from "@storybook/react-vite";

// Sample address suggestions shown once the user types 3+ characters.
const ADDRESS_SUGGESTIONS = [
  "123 Dale Springs, Apt B, Binsport, NC, 17032",
  "123 Dale Springs, Apt C, Binsport, NC, 17032",
  "123 Dale Avenue, Binsport, NC, 17033",
];

type AddressBarProps = {
  buttonVariant?: "primary_brand" | "primary_inverse" | "secondary";
  ctaLabel?: string;
  desktopPrompt?: string;
  mobilePrompt?: string;
  placeholder?: string;
  promptTextClass?: string;
};

// Shared address bar (title + location input + check-plans button) used across
// the stories. The button variant is configurable so themed variants (e.g.
// Yellow) can render an inverse/navy CTA.
const AddressBar = ({
  buttonVariant = "primary_brand",
  ctaLabel = "Check plans",
  desktopPrompt = "To see plans for your address, check availability",
  mobilePrompt = "Show me the best offers in my area",
  placeholder = "123 Dale Springs, Apt B, Binsport, NC, 17032",
  promptTextClass = "text-text",
}: AddressBarProps) => {
  const [query, setQuery] = useState("");
  const showResults = query.trim().length >= 3;

  // Mobile "Go" button reuses the BrandButton color tokens but is rendered as a
  // plain 56x56 button so it can honor the fixed Figma width (the shared Button
  // has a baked-in 60px horizontal padding that cannot be overridden here).
  const goVariantClass =
    buttonVariant === "primary_inverse"
      ? "bg-bg-fill-inverse text-text-inverse"
      : buttonVariant === "secondary"
        ? "border border-border-secondary-on-bg-fill bg-bg-fill-secondary text-text"
        : "bg-bg-fill-brand text-text-brand-on-bg-fill";

  return (
    <div className="flex w-full max-w-[1440px] flex-col items-center justify-center gap-4 py-[10px] lg:flex-row lg:gap-6">
      <span
        className={`label1 w-full whitespace-nowrap text-center text-base lg:hidden ${promptTextClass}`}
      >
        {mobilePrompt}
      </span>
      <span
        className={`label1 hidden lg:block lg:w-auto lg:shrink-0 lg:whitespace-nowrap lg:text-right ${promptTextClass}`}
      >
        {desktopPrompt}
      </span>
      <div className="flex w-full items-stretch gap-2 lg:contents">
        <div className="relative flex min-w-0 flex-1 flex-col items-start justify-center gap-1 lg:max-w-[480px] lg:flex-1">
          <div className="flex h-14 w-full items-center gap-2 self-stretch rounded-input border border-input-border bg-input-bg-surface px-4 focus-within:border-2 focus-within:border-input-border-selected lg:h-[60px]">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="20"
              height="20"
              viewBox="0 0 24 24"
              fill="#16a34a"
              aria-hidden="true"
              className="shrink-0"
            >
              <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1 1 12 6.5a2.5 2.5 0 0 1 0 5z" />
            </svg>
            <input
              type="text"
              value={query}
              onChange={event => setQuery(event.target.value)}
              placeholder={placeholder}
              className="body2 w-full min-w-0 bg-transparent text-text outline-none placeholder:text-input-text-placeholder"
            />
          </div>
          {showResults && (
            <div className="absolute left-0 top-full z-10 mt-1 flex w-full flex-col items-center rounded-lg bg-bg-surface shadow-[1px_1px_16px_0_rgba(0,0,0,0.12),-1px_-1px_16px_0_#FFF]">
              {ADDRESS_SUGGESTIONS.map(address => (
                <button
                  key={address}
                  type="button"
                  className="body2 flex w-full items-center gap-2 px-4 py-3 text-left text-text hover:bg-bg-surface-secondary"
                >
                  <svg
                    xmlns="http://www.w3.org/2000/svg"
                    width="20"
                    height="20"
                    viewBox="0 0 24 24"
                    fill="#757575"
                    aria-hidden="true"
                    className="shrink-0"
                  >
                    <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1 1 12 6.5a2.5 2.5 0 0 1 0 5z" />
                  </svg>
                  <span className="truncate">{address}</span>
                </button>
              ))}
            </div>
          )}
        </div>
        <button
          type="button"
          className={`flex h-14 w-14 shrink-0 items-center justify-center gap-2 rounded-button text-lg font-black lg:hidden ${goVariantClass}`}
        >
          Go
        </button>
        <div className="hidden lg:block">
          <Button
            showButtonAs="solid"
            buttonVariant={buttonVariant}
            buttonLabel={ctaLabel}
            size={{ base: "large" }}
          />
        </div>
      </div>
    </div>
  );
};

// Story args extend the banner props with the editable mock fields so the
// address bar text and CTA can be controlled from the Storybook controls
// instead of being hardcoded.
type StoryArgs = ComponentProps<typeof AddressInputBanner> & {
  ctaLabel?: string;
  ctaButtonVariant?: "primary_brand" | "primary_inverse" | "secondary";
  mobilePrompt?: string;
  placeholder?: string;
};

const meta: Meta<StoryArgs> = {
  title: "Contentful Blocks/AddressInputBanner",
  component: AddressInputBanner,
  tags: ["autodocs"],
  // Provide a tall, scrollable canvas so the sticky banner's `navHeight` offset
  // is observable — CSS `position: sticky` only applies its `top` value while
  // scrolling within a container taller than the viewport.
  decorators: [
    Story => (
      <div className="min-h-[150vh]">
        <Story />
        <p className="body2 p-6 text-text">
          Scroll to see the banner stick at the top (offset by navHeight).
        </p>
      </div>
    ),
  ],
  parameters: {
    layout: "fullscreen",
    docs: {
      page: DocsPage,
      description: {
        component:
          "A sticky address input banner component that supports multiple themed variants and an optional CTA. It can be shown/hidden dynamically and adjusts positioning based on the navigation height.",
      },
    },
  },
  argTypes: {
    title: {
      control: { type: "text" },
      description: "Title shown as the desktop heading next to the input",
    },
    variant: {
      control: { type: "select" },
      options: ["yellow", "white", "navy", "green"],
      description: "Themed color variant of the banner",
    },
    navHeight: {
      control: { type: "number" },
      description: "Navigation height used to offset the sticky position",
    },
    isVisible: {
      control: { type: "boolean" },
      description: "Whether the banner is rendered",
    },
    ctaLabel: {
      control: { type: "text" },
      description: "CTA button label",
    },
    ctaButtonVariant: {
      control: { type: "select" },
      options: ["primary_brand", "primary_inverse", "secondary"],
      description: "CTA button variant",
    },
    mobilePrompt: {
      control: { type: "text" },
      description: "Prompt text shown on mobile",
    },
    placeholder: {
      control: { type: "text" },
      description: "Address input placeholder text",
    },
    entryName: {
      table: { disable: true },
    },
    cta: {
      table: { disable: true },
    },
  },
  args: {
    title: "To see plans for your address, check availability",
    variant: "yellow",
    isVisible: true,
    navHeight: 0,
    ctaLabel: "Check plans",
    mobilePrompt: "Show me the best offers in my area",
    placeholder: "123 Dale Springs, Apt B, Binsport, NC, 17032",
  },
  render: ({
    title,
    ctaLabel,
    ctaButtonVariant,
    mobilePrompt,
    placeholder,
    renderCheckPlans,
    cta,
    ...args
  }) => {
    // Dark-background variants need light prompt text for contrast.
    const promptTextClass =
      args.variant === "navy" || args.variant === "green"
        ? "text-white"
        : "text-text";

    // Derive the CTA button variant from the banner variant so it adapts when
    // the variant control changes. An explicit ctaButtonVariant still overrides.
    const effectiveButtonVariant =
      ctaButtonVariant ??
      (args.variant === "yellow" || args.variant === "green"
        ? "primary_inverse"
        : "primary_brand");

    // Keep the cta's buttonVariant in sync with the effective variant so the
    // ctaButtonVariant control also drives stories that supply their own cta
    // and renderCheckPlans (e.g. WithCta).
    const effectiveCta = cta
      ? { ...cta, buttonVariant: effectiveButtonVariant }
      : cta;

    return (
      <AddressInputBanner
        {...args}
        cta={effectiveCta}
        title=""
        renderCheckPlans={
          renderCheckPlans ??
          (() => (
            <AddressBar
              buttonVariant={effectiveButtonVariant}
              ctaLabel={ctaLabel}
              desktopPrompt={title}
              mobilePrompt={mobilePrompt}
              placeholder={placeholder}
              promptTextClass={promptTextClass}
            />
          ))
        }
      />
    );
  },
};

export default meta;
type Story = StoryObj<typeof meta>;

// Default banner using all the default entries from meta args
export const Default: Story = {
  args: {},
  parameters: {
    docs: {
      description: {
        story:
          "Default AddressInputBanner rendered with all the default entries (yellow variant, visible, with the address bar CTA).",
      },
    },
  },
};

// Yellow variant
export const Yellow: Story = {
  args: {
    variant: "yellow",
  },
};

// White variant
export const White: Story = {
  args: {
    variant: "white",
  },
};

// Navy variant
export const Navy: Story = {
  args: {
    variant: "navy",
  },
};

// Green variant
export const Green: Story = {
  args: {
    variant: "green",
  },
};

// Hidden banner renders nothing
export const Hidden: Story = {
  args: {
    isVisible: false,
  },
  parameters: {
    docs: {
      description: {
        story:
          "When isVisible is false, the banner renders nothing to the DOM.",
      },
    },
  },
};

// Banner supplied with an explicit cta prop. It falls back to the default
// address bar render, so it looks consistent with the other variant stories
// while still exercising the cta overrides.
export const WithCta: Story = {
  args: {
    cta: {
      buttonLabel: "Check plans",
      buttonVariant: "primary_brand",
      showButtonAs: "solid",
    },
  },
  parameters: {
    docs: {
      description: {
        story:
          "AddressInputBanner rendered with an explicit `cta` prop. It uses the default address bar render, so it looks consistent with the other variant stories while still exercising the `cta` overrides.",
      },
    },
  },
};

// Demonstrates the sticky positioning offset with a non-zero navHeight value.
// A mock fixed nav bar is layered on top of the shared scrollable canvas so the
// banner can be seen sticking directly below it once the page scrolls.
export const CustomNavHeight: Story = {
  args: {
    navHeight: 80,
  },
  decorators: [
    (Story, context) => {
      // Keep the mock nav height/label in sync with the navHeight control.
      const navHeight = (context.args.navHeight as number | undefined) ?? 80;
      return (
        <div className="relative min-h-[150vh]">
          {/* Mock fixed navigation bar that matches the navHeight control */}
          <div
            style={{ height: navHeight }}
            className="fixed left-0 right-0 top-0 z-[90] flex w-full items-center justify-center overflow-hidden whitespace-nowrap bg-bg-fill-inverse text-xs text-text-inverse"
          >
            {`Mock Navigation (${navHeight}px)`}
          </div>
          {/* Spacer so content starts below the fixed nav */}
          <div style={{ height: navHeight }} />
          <Story />
          {/* Tall content in the SAME container as the banner so it has room to
              stick while scrolling. */}
          <div className="flex h-[120vh] items-start justify-center p-6 text-text">
            {`Scroll — the banner stays pinned ${navHeight}px from the top, below the nav.`}
          </div>
        </div>
      );
    },
  ],
  parameters: {
    docs: {
      description: {
        story:
          "Demonstrates the sticky positioning with a non-zero `navHeight` (80px), which offsets the banner's `top` value so it sticks below a fixed navigation bar. Scroll the canvas to see the offset take effect.",
      },
    },
  },
};
