'use client';

import React, { useState, useEffect, useCallback } from 'react';
import { createPortal } from 'react-dom';
import { PhotoWithFallback, Carousel, Modal, MarkdownRenderer, Button } from '../elements';
import type { OfferPublic } from '../../types/api/offer';
import type { Service, ServiceItem } from '../../types/api/service';
import type { Package } from '../../types/api/package';
import type { WebsitePhotos } from '../../types/api/website-photos';
import type { CompanyInformation } from '../../types/api/company-information';
import type { PhotoAttachment } from '../../types/api/photos';
import { useImageCycle } from '../../lib/hooks/use-image-cycle';

const SERVICE_MENU_MODAL_ROOT_ID = 'service-menu-modal-root';

/** Package with category names and first-service description fallback (derived from services). */
export interface PackageForMenu extends Package {
  category_names?: string[];
  first_service_description_markdown?: string | null;
}

export interface ServiceMenuSectionProps {
  title?: string;
  subtitle?: string;
  packages?: Package[] | null;
  /** Services (used to derive service items for the third row). */
  services?: Service[] | null;
  websitePhotos?: WebsitePhotos | null;
  companyInformation?: CompanyInformation | null;
  viewAllHref?: string;
  /** Label for the View All button (e.g. "View All"). */
  viewAllText?: string;
  /** Customer-friendly label for the third row (individual service items). Default "Treatments". */
  servicesRowTitle?: string;
  /** 'section' = compact header + carousel (e.g. home). 'page' = hero + grid (e.g. /service-menu). */
  variant?: 'section' | 'page';
}

/** Service item with parent service info for display in the Service Menu (fallback description from service when item has none). */
export interface ServiceItemWithService extends ServiceItem {
  service_name?: string;
  service_summary?: string | null;
  service_description_markdown?: string | null;
}

type DetailItem =
  | { type: 'package'; item: PackageForMenu }
  | { type: 'service_item'; item: ServiceItemWithService };

function getActivePublicOffers(offers: OfferPublic[] | undefined | null): OfferPublic[] {
  if (!Array.isArray(offers) || offers.length === 0) return [];
  return offers.filter((o) => o.active !== false && o.expired !== true);
}

/** Card with image area. Cycles through photos every N ms with a simple crossfade. */
function GridCardWithImage({
  photoAttachments,
  fallbackId,
  fallbackAlt,
  title,
  subtitle,
  children,
  onClick,
  websitePhotos,
  companyInformation,
  cycleSeed,
  hasSpecial,
}: {
  photoAttachments?: PhotoAttachment[];
  fallbackId: string | number;
  fallbackAlt: string;
  title: string;
  subtitle?: string | null;
  children?: React.ReactNode;
  onClick: () => void;
  websitePhotos?: WebsitePhotos | null;
  companyInformation?: CompanyInformation | null;
  cycleSeed?: string;
  /** Show a small “Special” badge when this package or treatment has active offers. */
  hasSpecial?: boolean;
}) {
  const seed = cycleSeed ?? String(fallbackId);
  const { list, currentIndex, nextIndex, transitioning } = useImageCycle(photoAttachments, seed);

  const displayAlt = list[currentIndex]?.alt || fallbackAlt;
  const singleUrl = list[0]?.url;

  return (
    <button
      type="button"
      onClick={onClick}
      className="flex flex-col h-full w-full text-left group block rounded-lg outline-none focus-visible:ring-2 focus-visible:ring-brand-accent focus-visible:ring-offset-2"
    >
      <div className="w-full h-36 overflow-hidden rounded-lg mb-3 relative">
        {hasSpecial ? (
          <span
            className="absolute top-2 right-2 z-10 rounded-full border border-brand-accent/40 bg-secondary/95 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-brand-accent shadow-sm backdrop-blur-sm"
            aria-label="Has special offer"
          >
            Special
          </span>
        ) : null}
        {list.length === 0 ? (
          <PhotoWithFallback
            item={undefined}
            fallbackId={fallbackId}
            alt={fallbackAlt}
            className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
            websitePhotos={websitePhotos}
            companyInformation={companyInformation}
          />
        ) : list.length === 1 && singleUrl ? (
          // eslint-disable-next-line @next/next/no-img-element -- dynamic API URLs; next/image not configured for external host
          <img
            src={singleUrl}
            alt={displayAlt}
            className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
          />
        ) : (
          <>
            {/* Only animate opacity when transitioning; when settling, snap so we don't double-fade */}
            <div
              className={`absolute inset-0 ${transitioning ? 'transition-opacity duration-[600ms] ease-in-out' : 'transition-none'}`}
              style={{ opacity: transitioning ? 0 : 1 }}
            >
              {/* eslint-disable-next-line @next/next/no-img-element -- dynamic API URLs */}
              <img
                src={list[currentIndex]?.url}
                alt={list[currentIndex]?.alt ?? displayAlt}
                className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
              />
            </div>
            <div
              className={`absolute inset-0 ${transitioning ? 'transition-opacity duration-[600ms] ease-in-out' : 'transition-none'}`}
              style={{ opacity: transitioning ? 1 : 0 }}
            >
              {/* eslint-disable-next-line @next/next/no-img-element -- dynamic API URLs */}
              <img
                src={list[nextIndex]?.url}
                alt={list[nextIndex]?.alt ?? displayAlt}
                className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
              />
            </div>
          </>
        )}
      </div>
      {subtitle && (
        <p className="text-xs font-medium text-fg-secondary uppercase tracking-wide line-clamp-1">
          {subtitle}
        </p>
      )}
      <h4 className="font-display text-base font-normal text-fg-primary mt-1 group-hover:underline line-clamp-2">
        {title}
      </h4>
      {children}
    </button>
  );
}

/** Single carousel row with small cards. Title and prev/next on one line to avoid excess spacing. */
function CarouselRow<T>({
  rowTitle,
  items,
  renderItem,
}: {
  rowTitle: string;
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
}) {
  if (!items?.length) return null;
  return (
    <div className="mb-12 last:mb-0">
      <Carousel.Root opts={{ align: 'start', loop: true }}>
        <div className="flex items-center justify-between gap-4 mb-2">
          <h3 className="font-display text-2xl font-normal text-fg-primary md:text-3xl">
            {rowTitle}
          </h3>
          <div className="flex gap-2 flex-shrink-0">
            <Carousel.PrevTrigger className="rounded-full p-1.5 border border-secondary hover:bg-primary_hover transition-colors disabled:opacity-30 disabled:cursor-not-allowed">
              <svg className="w-5 h-5 text-fg-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /></svg>
            </Carousel.PrevTrigger>
            <Carousel.NextTrigger className="rounded-full p-1.5 border border-secondary hover:bg-primary_hover transition-colors disabled:opacity-30 disabled:cursor-not-allowed">
              <svg className="w-5 h-5 text-fg-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /></svg>
            </Carousel.NextTrigger>
          </div>
        </div>
        <Carousel.Content className="-ml-3">
          {items.map((item, index) => (
            <Carousel.Item key={(item as { id?: number }).id ?? index} className="pl-3 basis-[70%] sm:basis-1/2 md:basis-1/3 lg:basis-1/4">
              {renderItem(item, index)}
            </Carousel.Item>
          ))}
        </Carousel.Content>
      </Carousel.Root>
    </div>
  );
}

/** Grid row: same section title + items in a responsive grid (top to bottom, left to right). */
function GridRow<T>({
  rowTitle,
  items,
  renderItem,
}: {
  rowTitle: string;
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
}) {
  if (!items?.length) return null;
  return (
    <div className="mb-12 last:mb-0">
      <h3 className="font-display text-2xl font-normal text-fg-primary mb-3 md:text-3xl">
        {rowTitle}
      </h3>
      <div className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
        {items.map((item, index) => (
          <div key={(item as { id?: number }).id ?? index} className="min-w-0 flex">
            {renderItem(item, index)}
          </div>
        ))}
      </div>
    </div>
  );
}

/** One menu block: either carousel or grid, same title + cards. */
function MenuBlock<T>({
  rowTitle,
  items,
  renderItem,
  layout,
}: {
  rowTitle: string;
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  layout: 'carousel' | 'grid';
}) {
  if (!items?.length) return null;
  return layout === 'carousel' ? (
    <CarouselRow rowTitle={rowTitle} items={items} renderItem={renderItem} />
  ) : (
    <GridRow rowTitle={rowTitle} items={items} renderItem={renderItem} />
  );
}

function formatPriceCents(cents: number | null | undefined): string | null {
  if (cents == null) return null;
  return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(cents / 100);
}

/** Section heading inside modal for consistent structure */
function ModalSection({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div>
      <h3 className="font-display text-sm font-semibold text-fg-primary uppercase tracking-wide mb-2">{title}</h3>
      {children}
    </div>
  );
}

function ActiveOffersCallout({ offers }: { offers: OfferPublic[] }) {
  if (!offers.length) return null;
  return (
    <div className="rounded-xl border border-secondary bg-secondary/25 p-4 md:p-5 space-y-3 ring-1 ring-brand-accent/25">
      <p className="text-xs font-semibold uppercase tracking-wide text-brand-accent">Current specials</p>
      <ul className="space-y-3 list-none m-0 p-0">
        {offers.map((o) => (
          <li
            key={o.id}
            className="rounded-lg border border-secondary border-l-[3px] border-l-brand-accent bg-primary_hover/30 p-4"
          >
            <p className="font-display text-base font-medium text-fg-primary">{o.name}</p>
            {o.value_terms ? (
              <p className="font-body text-sm text-brand-accent mt-1 font-medium">{o.value_terms}</p>
            ) : null}
            {o.description ? (
              <p className="font-body text-sm text-fg-secondary mt-2 leading-relaxed">{o.description}</p>
            ) : null}
            {o.expires_at ? (
              <p className="text-xs text-tertiary mt-2">
                Ends {new Date(o.expires_at).toLocaleDateString(undefined, { dateStyle: 'medium' })}
              </p>
            ) : null}
          </li>
        ))}
      </ul>
    </div>
  );
}

/** Modal body for package or service item (specials shown inline when present). */
function DetailModalContent({
  detail,
  serviceItems = [],
}: {
  detail: DetailItem;
  services?: Service[];
  packages?: PackageForMenu[];
  serviceItems?: ServiceItemWithService[];
}) {
  if (detail.type === 'package') {
    const p = detail.item;
    const categoryLine = p.category_names?.length ? p.category_names.join(' | ') : null;
    const descriptionMarkdown = p.description_markdown || p.summary || p.first_service_description_markdown;
    const packageSpecials = getActivePublicOffers(p.offers);

    return (
      <div className="space-y-6">
        {categoryLine && (
          <p className="text-sm font-medium text-fg-secondary uppercase tracking-wide">{categoryLine}</p>
        )}
        <ActiveOffersCallout offers={packageSpecials} />
        {descriptionMarkdown && (
          <ModalSection title="Package details">
            <div className="prose prose-sm font-body text-fg-primary max-w-none">
              <MarkdownRenderer content={descriptionMarkdown} />
            </div>
          </ModalSection>
        )}

        {p.package_items && p.package_items.length > 0 && (
          <ModalSection title="What's included">
            <ul className="space-y-4">
              {p.package_items.map((pi, i) => {
                const fullItem = pi.service_item?.id != null ? serviceItems.find((si) => si.id === pi.service_item!.id) : null;
                const name = fullItem?.name ?? pi.service_item?.name ?? 'Item';
                const desc = fullItem?.summary ?? fullItem?.description_markdown ?? pi.service_item?.summary;
                return (
                  <li key={pi.service_item?.id ?? i} className="border border-secondary rounded-lg p-4 bg-secondary/20">
                    <p className="font-display font-medium text-fg-primary">
                      {pi.quantity > 1 && `${pi.quantity}× `}{name}
                    </p>
                    {desc && (
                      <div className="mt-2 prose prose-sm font-body text-fg-secondary max-w-none">
                        {typeof desc === 'string' && !desc.includes('\n') && !desc.match(/[#*\[\]]/) ? (
                          <p>{desc}</p>
                        ) : (
                          <MarkdownRenderer content={desc} />
                        )}
                      </div>
                    )}
                  </li>
                );
              })}
            </ul>
          </ModalSection>
        )}

        {p.pricing_info && (
          <ModalSection title="Pricing">
            <div className="rounded-lg border border-secondary bg-secondary/40 p-4">
              <div className="prose prose-sm font-body text-fg-primary max-w-none">
                <MarkdownRenderer content={p.pricing_info} />
              </div>
            </div>
          </ModalSection>
        )}
      </div>
    );
  }

  const si = detail.item;
  const priceStr = formatPriceCents(si.price_cents);
  const summary = si.summary || si.service_summary;
  const descriptionMarkdown = si.description_markdown || si.service_description_markdown;
  const itemSpecials = getActivePublicOffers(si.offers);

  return (
    <div className="space-y-6">
      {si.service_name && (
        <p className="text-sm font-medium text-fg-secondary uppercase tracking-wide">{si.service_name}</p>
      )}
      <ActiveOffersCallout offers={itemSpecials} />

      {priceStr && (
        <ModalSection title="Price">
          <p className="font-display text-lg font-normal text-fg-primary">{priceStr}</p>
          {si.duration_minutes != null && si.duration_minutes > 0 && (
            <p className="font-body text-sm text-fg-secondary mt-1">Duration: {si.duration_minutes} min</p>
          )}
        </ModalSection>
      )}

      {summary && (
        <ModalSection title="Overview">
          <p className="font-body text-fg-primary">{summary}</p>
        </ModalSection>
      )}

      {descriptionMarkdown && (
        <ModalSection title="Full description">
          <div className="prose prose-sm font-body text-fg-primary max-w-none">
            <MarkdownRenderer content={descriptionMarkdown} />
          </div>
        </ModalSection>
      )}

      {si.pricing_info && (
        <ModalSection title="Pricing details">
          <div className="rounded-lg border border-secondary bg-secondary/40 p-4">
            <div className="prose prose-sm font-body text-fg-primary max-w-none">
              <MarkdownRenderer content={si.pricing_info} />
            </div>
          </div>
        </ModalSection>
      )}
    </div>
  );
}

/**
 * Service Menu: packages and treatments (service items). Specials appear as card badges and in modals.
 */
export function ServiceMenuSection({
  title = 'Service Menu',
  subtitle,
  packages = null,
  services = null,
  websitePhotos,
  companyInformation,
  viewAllHref,
  viewAllText = 'View All',
  servicesRowTitle = 'Treatments',
  variant = 'section',
}: ServiceMenuSectionProps) {
  const packageList = React.useMemo(
    () => (Array.isArray(packages) ? packages : []),
    [packages]
  );
  const serviceList = React.useMemo(
    () => (Array.isArray(services) ? services : []),
    [services]
  );

  const serviceItemIdToService = React.useMemo(() => {
    const m = new Map<number, Service>();
    serviceList.forEach((s) => (s.service_items || []).forEach((si) => m.set(si.id, s)));
    return m;
  }, [serviceList]);

  const packagesForMenu: PackageForMenu[] = React.useMemo(
    () =>
      packageList.map((pkg) => {
        const category_names = [
          ...new Set(
            (pkg.package_items || [])
              .map((pi) => serviceItemIdToService.get(pi.service_item?.id ?? 0)?.name)
              .filter(Boolean) as string[]
          ),
        ];
        const firstPi = pkg.package_items?.[0];
        const firstService = firstPi ? serviceItemIdToService.get(firstPi.service_item?.id ?? 0) : undefined;
        const first_service_description_markdown =
          firstService?.description_markdown || firstService?.summary || null;
        return { ...pkg, category_names, first_service_description_markdown };
      }),
    [packageList, serviceItemIdToService]
  );

  const serviceItemsForMenu: ServiceItemWithService[] = serviceList.flatMap((s) =>
    (s.service_items || []).map((si) => ({
      ...si,
      service_name: s.name,
      service_summary: s.summary,
      service_description_markdown: s.description_markdown,
    }))
  );
  const hasAny = packagesForMenu.length > 0 || serviceItemsForMenu.length > 0;

  const CAROUSEL_MIN = 3;
  // Page variant: always grid (full list, no carousel). Section variant: carousel when >=3 items else grid.
  const isPage = variant === 'page';
  const packagesLayout: 'carousel' | 'grid' = isPage ? 'grid' : (packagesForMenu.length >= CAROUSEL_MIN ? 'carousel' : 'grid');
  const serviceItemsLayout: 'carousel' | 'grid' = isPage ? 'grid' : (serviceItemsForMenu.length >= CAROUSEL_MIN ? 'carousel' : 'grid');

  const [detailItem, setDetailItem] = useState<DetailItem | null>(null);
  const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null);

  useEffect(() => {
    let el = document.getElementById(SERVICE_MENU_MODAL_ROOT_ID);
    if (!el) {
      el = document.createElement('div');
      el.id = SERVICE_MENU_MODAL_ROOT_ID;
      document.body.appendChild(el);
    }
    // Portal root: set DOM ref once on mount (standard createPortal pattern)
    // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional: portal mount node
    setPortalTarget(el);
    return () => { el?.remove(); };
  }, []);

  const closeModal = useCallback(() => setDetailItem(null), []);

  const renderPackageCard = useCallback(
    (pkg: PackageForMenu, index: number) => {
      const categorySubtitle = pkg.category_names?.length ? pkg.category_names.join(' | ') : null;
      const cardDesc = pkg.description_markdown || pkg.summary || pkg.first_service_description_markdown;
      const plainDesc = cardDesc ? cardDesc.replace(/[#*`\[\]]/g, '').replace(/\n+/g, ' ').trim() : '';
      const descText = plainDesc.length > 120 ? plainDesc.slice(0, 120) + '…' : plainDesc;
      const hasSpecial = getActivePublicOffers(pkg.offers).length > 0;
      return (
        <GridCardWithImage
          photoAttachments={pkg.photo_attachments}
          fallbackId={`pkg-${pkg.id}`}
          fallbackAlt={pkg.name}
          title={pkg.name}
          subtitle={categorySubtitle}
          onClick={() => setDetailItem({ type: 'package', item: pkg })}
          websitePhotos={websitePhotos}
          companyInformation={companyInformation}
          cycleSeed={`pkg-${pkg.id}-${index}`}
          hasSpecial={hasSpecial}
        >
          {descText && (
            <p className="font-body text-sm text-tertiary mt-1 line-clamp-2">{descText}</p>
          )}
        </GridCardWithImage>
      );
    },
    [websitePhotos, companyInformation]
  );

  const renderServiceItemCard = useCallback(
    (si: ServiceItemWithService, index: number) => (
      <GridCardWithImage
        photoAttachments={si.photo_attachments}
        fallbackId={`service-item-${si.id}`}
        fallbackAlt={si.photo_attachments?.[0]?.photo?.title || si.name}
        title={si.name}
        subtitle={si.service_name ?? null}
        onClick={() => setDetailItem({ type: 'service_item', item: si })}
        websitePhotos={websitePhotos}
        companyInformation={companyInformation}
        cycleSeed={`service-item-${si.id}-${index}`}
        hasSpecial={getActivePublicOffers(si.offers).length > 0}
      >
        {formatPriceCents(si.price_cents) && (
          <p className="font-body text-sm font-medium text-fg-primary mt-1">
            {formatPriceCents(si.price_cents)}
          </p>
        )}
        {(() => {
          const desc = si.summary || si.description_markdown || si.service_summary || si.service_description_markdown;
          if (!desc) return null;
          const plain = desc.replace(/[#*`\[\]]/g, '').replace(/\n+/g, ' ').trim();
          const text = plain.length > 120 ? plain.slice(0, 120) + '…' : plain;
          return (
            <p className="font-body text-sm text-tertiary mt-1 line-clamp-2">
              {text}
            </p>
          );
        })()}
      </GridCardWithImage>
    ),
    [websitePhotos, companyInformation]
  );

  if (!hasAny) return null;

  const modalTitle = detailItem ? detailItem.item.name : '';

  return (
    <section className={variant === 'page' ? 'py-12 md:py-20' : 'py-12 md:py-16'}>
      <div className="mx-auto max-w-container px-4 md:px-8">
        {/* Section variant: centered title/subtitle in-section. Page variant: hero is provided by the page (GenericTextHero). */}
        {variant === 'section' && (
          <div className="mx-auto max-w-3xl text-center mb-12 md:mb-16">
            {title && (
              <h2 className="font-display text-4xl font-normal text-fg-primary md:text-5xl">
                {title}
              </h2>
            )}
            {subtitle && (
              <p className="mt-4 font-display text-lg leading-relaxed text-tertiary md:text-xl max-w-3xl mx-auto">
                {subtitle}
              </p>
            )}
          </div>
        )}

        <MenuBlock
          rowTitle="Packages"
          items={packagesForMenu}
          layout={packagesLayout}
          renderItem={renderPackageCard}
        />

        <MenuBlock
          rowTitle={servicesRowTitle}
          items={serviceItemsForMenu}
          layout={serviceItemsLayout}
          renderItem={renderServiceItemCard}
        />

        {variant === 'section' && viewAllHref && viewAllText && (
          <div className="mt-12 text-center">
            <Button href={viewAllHref} color="primary" size="md">
              {viewAllText}
            </Button>
          </div>
        )}
      </div>

      {portalTarget &&
        detailItem &&
        createPortal(
          <Modal
            isOpen={true}
            onClose={closeModal}
            title={modalTitle}
            maxWidth="2xl"
          >
            <DetailModalContent
              detail={detailItem}
              services={serviceList}
              packages={packagesForMenu}
              serviceItems={serviceItemsForMenu}
            />
          </Modal>,
          portalTarget
        )}
    </section>
  );
}
