import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react';
import type { CSSProperties } from 'react';
import MapPinSelector from './MapPinSelector';
import AddressForm from './AddressForm';
import PhoneStep from './PhoneStep';
import OtpStep from './OtpStep';
import SavedAddressStep from './SavedAddressStep';
import {
  createAddress,
  notifyAddressCompleted,
  logFunnelEvent,
  notifySessionAbandoned,
  fetchSessionOffer,
  scoreAddressRisk,
  selectAddress,
  verifyDigipin,
  type AddressRiskScore,
  type SessionOffer,
} from '../core/checkout-api';
import type { CheckoutEvent, CheckoutWidgetProps, CompleteAddress } from '../core/types';
import type { SavedAddress } from '../core/checkout-api';
import type { SessionContextPayload } from '../core/checkout-api';
import CodAvailabilityBanner from './CodAvailabilityBanner';
import PrepaidNudge from './PrepaidNudge';
import PaymentStep from './PaymentStep';
import EmailStep from './EmailStep';
import EmailOtpStep from './EmailOtpStep';
import { getWebDeviceContext } from '../core/device';
import { captureUtmFromUrl } from '../core/utm';
import '../styles/checkout.css';

// ─── Types ────────────────────────────────────────────────────────────────────

/** Full step machine — auth gate is phone, email, or none */
type Step =
  | 'phone'
  | 'otp'
  | 'email'
  | 'email-otp'
  | 'saved'
  | 'verify-address'
  | 'map'
  | 'form'
  | 'payment'
  | 'done';

interface ConfirmedLocation {
  lat: number;
  lng: number;
  digipin: string;
}

interface AuthState {
  mobile: string;
  sessionToken: string;
  sessionId: string;
  buyerId: string;
}

type MobileStepStatus = 'complete' | 'active' | 'pending';

interface MobileStepItem {
  id: 'auth' | 'address' | 'payment';
  label: string;
  status: MobileStepStatus;
}

// ─── Success screen ───────────────────────────────────────────────────────────

interface SuccessScreenProps {
  address: CompleteAddress;
  onEditAddress: () => void;
}

const SuccessScreen: React.FC<SuccessScreenProps> = ({ address, onEditAddress }) => (
  <div className="qr-success">
    <div className="qr-success__icon" aria-hidden="true">
      <svg viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
        <circle cx="26" cy="26" r="26" fill="var(--qr-success, #10b981)" opacity="0.12" />
        <circle cx="26" cy="26" r="20" fill="var(--qr-success, #10b981)" />
        <path
          d="M15 26l8 8 14-16"
          stroke="white"
          strokeWidth="3"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
      </svg>
    </div>
    <h3 className="qr-success__title">Address Saved!</h3>
    <p className="qr-success__address">{address.formattedAddress}</p>
    <div className="qr-success__meta">
      <span className="qr-digipin-badge qr-digipin-badge--inline">
        <span className="qr-digipin-badge__label">DigiPin</span>
        <span className="qr-digipin-badge__code">{address.digipin}</span>
      </span>
    </div>
    <button type="button" className="qr-btn qr-btn--ghost qr-btn--sm" onClick={onEditAddress}>
      Change Address
    </button>
  </div>
);

// ─── Step indicator ───────────────────────────────────────────────────────────

type AuthMode = 'phone' | 'email' | 'none';

function stepToIndex(step: Step, mode: AuthMode, withPayment: boolean): number {
  const normalizedStep = step === 'verify-address' ? 'map' : step;
  if (mode === 'none') {
    if (withPayment) {
      const order: Step[] = ['map', 'form', 'payment', 'done'];
      const idx = order.indexOf(normalizedStep);
      return idx === -1 ? 0 : idx;
    }
    return normalizedStep === 'map' ? 0 : normalizedStep === 'form' ? 1 : 2;
  }
  const order: Step[] =
    mode === 'email'
      ? withPayment
        ? ['email', 'email-otp', 'saved', 'map', 'form', 'payment', 'done']
        : ['email', 'email-otp', 'saved', 'map', 'form', 'done']
      : withPayment
        ? ['phone', 'otp', 'saved', 'map', 'form', 'payment', 'done']
        : ['phone', 'otp', 'saved', 'map', 'form', 'done'];
  const idx = order.indexOf(normalizedStep);
  return idx === -1 ? 0 : idx;
}

function totalSteps(mode: AuthMode, withPayment: boolean): number {
  if (mode === 'none') return withPayment ? 3 : 2;
  return withPayment ? 6 : 5;
}

/** Merge merchant `brandColor` into CSS variables consumed by checkout.css */
function mergeBrandRootStyle(
  base: CSSProperties | undefined,
  brandColor: string | undefined
): CSSProperties | undefined {
  const trimmed = brandColor?.trim();
  if (!trimmed) return base;
  const vars: CSSProperties = {
    ['--qr-primary' as string]: trimmed,
    ['--qr-primary-dark' as string]: trimmed,
    ['--qr-border-focus' as string]: trimmed,
    ['--qr-primary-light' as string]: `color-mix(in srgb, ${trimmed} 18%, white)`,
  };
  return base ? { ...base, ...vars } : vars;
}

function savedAddressToComplete(saved: SavedAddress): CompleteAddress {
  return {
    digipin: saved.digipin!,
    lat: saved.lat!,
    lng: saved.lng!,
    state: saved.state,
    district: saved.district,
    division: '',
    locality: saved.locality,
    pincode: saved.pincode,
    delivery: saved.delivery_status,
    country: 'India',
    flatNumber: saved.flat_number,
    floorNumber: saved.floor_number,
    buildingName: saved.building_name,
    streetName: saved.street_name,
    formattedAddress: [
      saved.flat_number,
      saved.building_name,
      saved.street_name,
      saved.locality,
      saved.district,
      saved.state,
      saved.pincode,
    ]
      .filter(Boolean)
      .join(', '),
    label: (saved.label === 'work' || saved.label === 'other'
      ? saved.label
      : 'home') as CompleteAddress['label'],
    labelText: saved.label_text?.trim() || undefined,
    addressId: saved.id,
  };
}

// ─── Main Widget ──────────────────────────────────────────────────────────────

/**
 * QuantaRoute Checkout Widget
 *
 * Default (enablePhoneAuth = false):
 *   map → form → done
 *
 * With phone auth (enablePhoneAuth = true):
 *   phone → otp → saved → map → form → done
 *   (buyer can skip phone auth to go straight to map)
 */
const CheckoutWidget: React.FC<CheckoutWidgetProps> = ({
  apiKey,
  apiBaseUrl = 'https://api.quantaroute.com',
  onComplete,
  onError,
  defaultLat,
  defaultLng,
  theme = 'light',
  className = '',
  style,
  mapHeight = 'min(52dvh, 420px)',
  title = 'Add Delivery Address',
  mobilePresentation = 'auto',
  mobileStepLabels,
  indiaBoundaryUrl,
  enableSearch = true,
  mapStyle,
  embedMode,
  enablePhoneAuth = false,
  merchantId = '',
  supabaseFunctionBaseUrl = '',
  brandColor,
  logoUrl,
  headerBanner,
  onCheckoutEvent,
  enableCod = false,
  enableServiceabilityCheck = false,
  enableAddressIntelligence = false,
  showIndiaPostDeliveryWarning = false,
  serviceabilityBlockCheckout,
  codCartTotalPaise,
  codSessionToken = '',
  enableEmailAuth = false,
  variant,
  utmParams: utmParamsProp,
  enablePayment = false,
  paymentAmountPaise,
  onPaymentComplete,
  enablePrepaidNudge = false,
  prepaidNudgeCopy,
  prepaidNudgeCtaUrl,
  registerController,
  orderConfirmationUrl,
}) => {
  const authMode: AuthMode = enablePhoneAuth ? 'phone' : enableEmailAuth ? 'email' : 'none';
  const initialStep: Step = authMode === 'phone' ? 'phone' : authMode === 'email' ? 'email' : 'map';
  const withPayment = Boolean(enablePayment && paymentAmountPaise && supabaseFunctionBaseUrl);

  const [step, setStep] = useState<Step>(initialStep);
  const [confirmedLocation, setConfirmedLocation] = useState<ConfirmedLocation | null>(null);
  const [completedAddress, setCompletedAddress] = useState<CompleteAddress | null>(null);
  const [auth, setAuth] = useState<AuthState | null>(null);
  const [emailPending, setEmailPending] = useState('');
  const [emailCodeReused, setEmailCodeReused] = useState(false);
  const [codAllowed, setCodAllowed] = useState(false);
  const [sessionOffer, setSessionOffer] = useState<SessionOffer | null>(null);
  const [addressRisk, setAddressRisk] = useState<AddressRiskScore | null>(null);
  const [paymentMethod, setPaymentMethod] = useState<string | null>(null);
  const [verifyAddress, setVerifyAddress] = useState<SavedAddress | null>(null);
  const [paymentBackStep, setPaymentBackStep] = useState<Step>('form');
  const stepRef = useRef(step);

  const utmParams = useMemo(
    () => utmParamsProp ?? captureUtmFromUrl(),
    [utmParamsProp]
  );

  const deviceCtx = useMemo(() => getWebDeviceContext(), []);

  const sessionContext: SessionContextPayload = useMemo(
    () => ({
      utm_source: utmParams.utm_source,
      utm_medium: utmParams.utm_medium,
      utm_campaign: utmParams.utm_campaign,
      device_type: deviceCtx.deviceType,
      platform: deviceCtx.platform,
      user_agent: deviceCtx.userAgent,
      experiment_variant: variant,
    }),
    [utmParams, deviceCtx, variant]
  );

  const checkoutConfig = {
    functionBaseUrl: supabaseFunctionBaseUrl,
    merchantId,
  };

  const codSession = auth?.sessionToken || codSessionToken;

  useEffect(() => {
    stepRef.current = step;
  }, [step]);

  const authChannel = (): CheckoutEvent['authChannel'] => {
    if (authMode === 'phone') return 'phone';
    if (authMode === 'email') return 'email';
    return 'none';
  };

  /** Fire client + server analytics without blocking the UI. */
  const emit = useCallback(
    (partial: Omit<CheckoutEvent, 'timestamp'>) => {
      const event: CheckoutEvent = {
        ...partial,
        sessionId: partial.sessionId ?? auth?.sessionId,
        buyerId: partial.buyerId ?? auth?.buyerId,
        authChannel: partial.authChannel ?? authChannel(),
        deviceType: partial.deviceType ?? deviceCtx.deviceType,
        platform: partial.platform ?? deviceCtx.platform,
        userAgent: partial.userAgent ?? deviceCtx.userAgent,
        experimentVariant: partial.experimentVariant ?? variant,
        timestamp: new Date().toISOString(),
      };

      if (onCheckoutEvent) {
        try {
          onCheckoutEvent(event);
        } catch {
          // Never let analytics crash the checkout
        }
      }

      if (merchantId && supabaseFunctionBaseUrl) {
        logFunnelEvent(
          checkoutConfig,
          {
            event_type: event.type,
            step: event.step,
            buyer_id: event.buyerId,
            auth_channel: event.authChannel,
            device_type: event.deviceType,
            platform: event.platform,
            user_agent: event.userAgent,
            payment_method: event.paymentMethod,
            experiment_variant: event.experimentVariant,
          },
          auth?.sessionToken
        ).catch(() => {/* silent */});
      }
    },
    [
      auth,
      authMode,
      deviceCtx,
      variant,
      onCheckoutEvent,
      merchantId,
      supabaseFunctionBaseUrl,
      checkoutConfig,
    ]
  );

  // Explicit abandon on tab close / navigate away
  useEffect(() => {
    if (!merchantId || !supabaseFunctionBaseUrl) return;

    const onLeave = () => {
      if (stepRef.current === 'done' || !auth?.sessionToken) return;
      notifySessionAbandoned(checkoutConfig, auth.sessionToken, stepRef.current).catch(() => {});
      emit({ type: 'session_abandoned', step: stepRef.current });
    };

    window.addEventListener('pagehide', onLeave);
    return () => window.removeEventListener('pagehide', onLeave);
  }, [auth?.sessionToken, merchantId, supabaseFunctionBaseUrl, checkoutConfig, emit]);

  // Fetch UTM-matched offer after auth
  useEffect(() => {
    if (!auth?.sessionToken || !merchantId || !supabaseFunctionBaseUrl) return;
    fetchSessionOffer(checkoutConfig, auth.sessionToken)
      .then(setSessionOffer)
      .catch(() => setSessionOffer(null));
  }, [auth?.sessionToken, merchantId, supabaseFunctionBaseUrl, checkoutConfig]);

  const rootStyle = useMemo(
    () => mergeBrandRootStyle(style as CSSProperties | undefined, brandColor),
    [style, brandColor]
  );

  // ── Phone step callbacks ──────────────────────────────────────────────────

  const handleOtpSent = (mobile: string) => {
    setAuth((prev) => ({ mobile, sessionToken: '', sessionId: '', buyerId: prev?.buyerId ?? '' }));
    emit({ type: 'otp_sent', step: 'otp', mobile });
    setStep('otp');
  };

  const handleVerified = (sessionToken: string, buyerId: string, sessionId?: string) => {
    setAuth({ mobile: auth?.mobile ?? '', sessionToken, sessionId: sessionId ?? '', buyerId });
    emit({ type: 'phone_verified', step: 'saved', buyerId, sessionId });
    setStep('saved');
  };

  const handleSkipAuth = () => {
    emit({ type: 'auth_skipped', step: 'map' });
    setStep('map');
  };

  // ── Email step callbacks ──────────────────────────────────────────────────

  const handleEmailOtpSent = (sentEmail: string, info?: { reused?: boolean }) => {
    setEmailPending(sentEmail);
    setEmailCodeReused(Boolean(info?.reused));
    emit({ type: 'email_otp_sent', step: 'email-otp', mobile: sentEmail });
    setStep('email-otp');
  };

  const handleEmailVerified = (sessionToken: string, buyerId: string, sessionId?: string) => {
    setAuth({ mobile: emailPending, sessionToken, sessionId: sessionId ?? '', buyerId });
    emit({ type: 'email_verified', step: 'saved', buyerId, sessionId });
    setStep('saved');
  };

  // ── Saved addresses callbacks ─────────────────────────────────────────────

  const handleSelectSavedAddress = async (saved: SavedAddress) => {
    if (!auth?.sessionToken) return;

    try {
      await selectAddress(checkoutConfig, auth.sessionToken, saved.id);
    } catch (err: unknown) {
      onError?.(err instanceof Error ? err : new Error('Failed to select address.'));
      return;
    }

    if (!saved.digipin) {
      setVerifyAddress(saved);
      emit({
        type: 'saved_address_selected',
        step: 'verify-address',
        buyerId: auth.buyerId,
      });
      setStep('verify-address');
      return;
    }

    emit({
      type: 'saved_address_selected',
      step: withPayment ? 'payment' : 'done',
      buyerId: auth.buyerId,
      digipin: saved.digipin,
    });

    notifyAddressCompleted(checkoutConfig, auth.sessionToken, {
      pincode: saved.pincode,
      digipin: saved.digipin,
      state: saved.state,
      district: saved.district,
      locality: saved.locality,
    }).catch(() => {/* silent */});

    await finishAddress(savedAddressToComplete(saved), { skipCreate: true, paymentBackStep: 'saved' });
  };

  const handlePaymentComplete = ({ paymentMethod, razorpayOrderId, razorpayPaymentId }: {
    paymentMethod: string;
    razorpayOrderId?: string;
    razorpayPaymentId?: string;
  }) => {
    setPaymentMethod(paymentMethod);
    emit({ type: 'payment_method_selected', step: 'done', paymentMethod });
    setStep('done');
    if (completedAddress) onComplete(completedAddress);
    onPaymentComplete?.({
      paymentMethod,
      sessionId: auth?.sessionId,
      razorpayOrderId,
      razorpayPaymentId,
    });
  };

  // ── Map step callbacks ────────────────────────────────────────────────────

  const handleLocationConfirm = (lat: number, lng: number, digipin: string) => {
    setConfirmedLocation({ lat, lng, digipin });
    emit({ type: 'location_confirmed', step: 'form', digipin, buyerId: auth?.buyerId });
    setStep('form');
  };

  // ── Form step callbacks ───────────────────────────────────────────────────

  const finishAddress = async (
    address: CompleteAddress,
    options?: { skipCreate?: boolean; paymentBackStep?: Step }
  ) => {
    let addressForCompletion: CompleteAddress = address;
    setCompletedAddress(addressForCompletion);
    setPaymentBackStep(options?.paymentBackStep ?? 'form');

    if (auth?.sessionToken) {
      notifyAddressCompleted(checkoutConfig, auth.sessionToken, {
        pincode: address.pincode,
        digipin: address.digipin,
        state: address.state,
        district: address.district,
        locality: address.locality,
      }).catch(() => {/* silent */});

      if (!options?.skipCreate && !address.addressId) {
        try {
          const saved = await createAddress(checkoutConfig, auth.sessionToken, {
            digipin: address.digipin,
            lat: address.lat,
            lng: address.lng,
            state: address.state,
            district: address.district,
            locality: address.locality,
            pincode: address.pincode,
            delivery_status: address.delivery,
            flat_number: address.flatNumber,
            floor_number: address.floorNumber,
            building_name: address.buildingName,
            street_name: address.streetName,
            full_address_json: address as unknown as Record<string, unknown>,
            label: address.label ?? 'home',
            label_text:
              address.label === 'other' ? (address.labelText?.trim() || null) : null,
            is_default: false,
          });
          addressForCompletion = { ...addressForCompletion, addressId: saved.id };
          setCompletedAddress(addressForCompletion);
        } catch {
          // Silent
        }
      }
    }

    if (enableAddressIntelligence && merchantId && supabaseFunctionBaseUrl && codSession) {
      try {
        const risk = await scoreAddressRisk(checkoutConfig, codSession, {
          address_id: addressForCompletion.addressId,
          digipin: addressForCompletion.digipin,
          lat: addressForCompletion.lat,
          lng: addressForCompletion.lng,
          state: addressForCompletion.state,
          district: addressForCompletion.district,
          locality: addressForCompletion.locality,
          pincode: addressForCompletion.pincode,
          delivery_status: addressForCompletion.delivery,
          flat_number: addressForCompletion.flatNumber,
          floor_number: addressForCompletion.floorNumber,
          building_name: addressForCompletion.buildingName,
          street_name: addressForCompletion.streetName,
        });
        setAddressRisk(risk);
        addressForCompletion = { ...addressForCompletion, addressRisk: risk };
        setCompletedAddress(addressForCompletion);
        emit({
          type: 'address_risk_scored',
          step: withPayment ? 'payment' : 'done',
          digipin: addressForCompletion.digipin,
          buyerId: auth?.buyerId,
          addressRiskLevel: risk.level,
          addressRiskScore: risk.score,
        });
      } catch {
        setAddressRisk(null);
      }
    } else {
      setAddressRisk(null);
    }

    emit({
      type: 'address_completed',
      step: withPayment ? 'payment' : 'done',
      digipin: addressForCompletion.digipin,
      buyerId: auth?.buyerId,
    });

    if (withPayment && codSession) {
      setStep('payment');
    } else if (withPayment && !codSession) {
      onError?.(new Error('enablePayment requires phone/email auth or a codSessionToken prop.'));
      setStep('done');
      onComplete(addressForCompletion);
      return;
    } else {
      setStep('done');
      onComplete(addressForCompletion);
    }
  };

  const handleAddressComplete = async (address: CompleteAddress) => {
    if (verifyAddress && auth?.sessionToken) {
      try {
        await verifyDigipin(checkoutConfig, auth.sessionToken, verifyAddress.id, {
          digipin: address.digipin,
          lat: address.lat,
          lng: address.lng,
          state: address.state,
          district: address.district,
          locality: address.locality,
          pincode: address.pincode,
          delivery_status: address.delivery,
          flat_number: address.flatNumber,
          floor_number: address.floorNumber,
          building_name: address.buildingName,
          street_name: address.streetName,
        });
        const complete: CompleteAddress = { ...address, addressId: verifyAddress.id };
        setVerifyAddress(null);
        await finishAddress(complete, { skipCreate: true });
      } catch (err: unknown) {
        onError?.(err instanceof Error ? err : new Error('Failed to verify delivery pin.'));
      }
      return;
    }
    await finishAddress(address);
  };

  const handleBack = (): boolean => {
    if (step === 'form') {
      setStep(verifyAddress ? 'verify-address' : 'map');
      return true;
    }
    if (step === 'verify-address') {
      setVerifyAddress(null);
      setConfirmedLocation(null);
      setStep('saved');
      return true;
    }
    if (step === 'map' && auth?.sessionToken) {
      setStep('saved');
      return true;
    }
    if (step === 'email-otp') {
      setStep('email');
      return true;
    }
    if (step === 'otp') {
      setStep('phone');
      return true;
    }
    if (step === 'payment') {
      setStep(paymentBackStep);
      return true;
    }
    return false;
  };

  useEffect(() => {
    if (!registerController) return;
    registerController({
      goBack: () => {
        const current = stepRef.current;
        if (current === 'form') {
          setStep(verifyAddress ? 'verify-address' : 'map');
          return true;
        }
        if (current === 'verify-address') {
          setVerifyAddress(null);
          setConfirmedLocation(null);
          setStep('saved');
          return true;
        }
        if (current === 'map' && auth?.sessionToken) {
          setStep('saved');
          return true;
        }
        if (current === 'email-otp') {
          setStep('email');
          return true;
        }
        if (current === 'otp') {
          setStep('phone');
          return true;
        }
        if (current === 'payment') {
          setStep(paymentBackStep);
          return true;
        }
        return false;
      },
      getStep: () => stepRef.current,
    });
  }, [registerController, auth?.sessionToken, verifyAddress, paymentBackStep]);

  const handleEditAddress = () => {
    emit({ type: 'widget_reset', step: initialStep });
    setStep(initialStep);
    setConfirmedLocation(null);
    setCompletedAddress(null);
    setAddressRisk(null);
    setVerifyAddress(null);
  };

  // ── Emit "step viewed" event on every step change ────────────────────────

  useEffect(() => {
    // OTP steps fire send events in handlers; only emit step-viewed here.
    const viewedMap: Partial<Record<Step, CheckoutEvent['type']>> = {
      phone: 'phone_step_viewed',
      email: 'email_step_viewed',
      saved: 'saved_addresses_viewed',
      'verify-address': 'verify_address_step_viewed',
      map: 'map_step_viewed',
      form: 'form_step_viewed',
      payment: 'payment_step_viewed',
    };
    const eventType = viewedMap[step];
    if (!eventType) return;
    emit({ type: eventType, step, buyerId: auth?.buyerId });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [step]);

  useEffect(() => {
    if (!enableEmailAuth || authMode !== 'email') return;
    if (merchantId && supabaseFunctionBaseUrl) return;
    onError?.(
      new Error(
        'enableEmailAuth requires merchantId and supabaseFunctionBaseUrl on CheckoutWidget.'
      )
    );
  }, [enableEmailAuth, authMode, merchantId, supabaseFunctionBaseUrl, onError]);

  const currentIndex = stepToIndex(step, authMode, withPayment);
  const total = totalSteps(authMode, withPayment);

  const activeMobileStep: MobileStepItem['id'] =
    step === 'payment'
      ? 'payment'
      : step === 'phone' || step === 'otp' || step === 'email' || step === 'email-otp'
        ? 'auth'
        : 'address';

  const authStepLabel =
    mobileStepLabels?.auth ?? (authMode === 'email' ? 'Email' : 'Mobile');

  const mobileSteps: MobileStepItem[] = [
    ...(authMode === 'none'
      ? []
      : [{
          id: 'auth' as const,
          label: authStepLabel,
          status: (activeMobileStep === 'auth'
            ? 'active'
            : 'complete') as MobileStepStatus,
        }]),
    {
      id: 'address',
      label: mobileStepLabels?.address ?? 'Address',
      status: (activeMobileStep === 'address'
        ? 'active'
        : activeMobileStep === 'payment'
          ? 'complete'
          : 'pending') as MobileStepStatus,
    },
    ...(withPayment
      ? [{
          id: 'payment' as const,
          label: mobileStepLabels?.payment ?? 'Pay',
          status: (activeMobileStep === 'payment' ? 'active' : 'pending') as MobileStepStatus,
        }]
      : []),
  ];

  const renderProgress = () => {
    const dots = Array.from({ length: total + 1 }, (_, i) => i);
    return (
      <div
        className="qr-progress"
        role="progressbar"
        aria-valuenow={currentIndex + 1}
        aria-valuemin={1}
        aria-valuemax={total + 1}
        aria-label={`Step ${currentIndex + 1} of ${total + 1}`}
      >
        {dots.map((i) => (
          <React.Fragment key={i}>
            <div className={`qr-progress__dot${currentIndex >= i ? ' qr-progress__dot--active' : ''}`} />
            {i < total && (
              <div className={`qr-progress__line${currentIndex > i ? ' qr-progress__line--active' : ''}`} />
            )}
          </React.Fragment>
        ))}
      </div>
    );
  };

  const renderMobileStepper = () => (
    <div
      className="qr-mobile-stepper"
      role="list"
      aria-label="Checkout progress"
    >
      {mobileSteps.map((item, index) => (
        <React.Fragment key={item.id}>
          <div
            className={`qr-mobile-stepper__item qr-mobile-stepper__item--${item.status}`}
            role="listitem"
            aria-current={item.status === 'active' ? 'step' : undefined}
          >
            <span className="qr-mobile-stepper__mark" aria-hidden="true">
              {item.status === 'complete' ? '✓' : index + 1}
            </span>
            <span className="qr-mobile-stepper__label">{item.label}</span>
          </div>
          {index < mobileSteps.length - 1 ? (
            <span className="qr-mobile-stepper__rail" aria-hidden="true" />
          ) : null}
        </React.Fragment>
      ))}
    </div>
  );

  // ── Render ────────────────────────────────────────────────────────────────

  return (
    <div
      className={`qr-checkout qr-checkout--${theme} qr-checkout--mobile-${mobilePresentation} ${className}`}
      style={rootStyle}
      data-testid="quantaroute-checkout"
    >
      {/* Widget header */}
      {step !== 'done' && (
        <>
          <div className="qr-header">
            <div className="qr-header__brand">
              {logoUrl ? (
                <img
                  src={logoUrl}
                  alt=""
                  className="qr-header__logo-img"
                  width={28}
                  height={28}
                  decoding="async"
                />
              ) : (
                <svg
                  className="qr-header__logo"
                  aria-hidden="true"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                >
                  <path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0118 0z" />
                  <circle cx="12" cy="10" r="3" />
                </svg>
              )}
              <span className="qr-header__title">{title}</span>
            </div>
            <div className="qr-header__progress-desktop">
              {renderProgress()}
            </div>
          </div>
          <div className="qr-header__progress-mobile">
            {renderMobileStepper()}
          </div>
          {headerBanner ? (
            <div className="qr-header-banner" role="region" aria-label="Merchant message">
              {headerBanner}
            </div>
          ) : null}
          {sessionOffer ? (
            <div className="qr-offer-banner" role="region" aria-label="Special offer">
              {sessionOffer.banner_copy}
              {sessionOffer.banner_cta_url ? (
                <>
                  {' '}
                  <a href={sessionOffer.banner_cta_url} target="_blank" rel="noopener noreferrer">
                    Learn more
                  </a>
                </>
              ) : null}
            </div>
          ) : null}
        </>
      )}

      {/* ── Email step ── */}
      {step === 'email' && enableEmailAuth && (
        <EmailStep
          onOtpSent={handleEmailOtpSent}
          onSkip={handleSkipAuth}
          functionBaseUrl={supabaseFunctionBaseUrl}
          merchantId={merchantId}
        />
      )}

      {/* ── Email OTP step ── */}
      {step === 'email-otp' && enableEmailAuth && emailPending && (
        <EmailOtpStep
          email={emailPending}
          codeReused={emailCodeReused}
          onVerified={handleEmailVerified}
          onBack={handleBack}
          functionBaseUrl={supabaseFunctionBaseUrl}
          merchantId={merchantId}
          sessionContext={sessionContext}
        />
      )}

      {/* ── Phone step ── */}
      {step === 'phone' && enablePhoneAuth && (
        <PhoneStep
          onOtpSent={handleOtpSent}
          onSkip={handleSkipAuth}
          functionBaseUrl={supabaseFunctionBaseUrl}
          merchantId={merchantId}
        />
      )}

      {/* ── OTP step ── */}
      {step === 'otp' && auth && (
        <OtpStep
          mobile={auth.mobile}
          onVerified={handleVerified}
          onBack={handleBack}
          functionBaseUrl={supabaseFunctionBaseUrl}
          merchantId={merchantId}
          sessionContext={sessionContext}
        />
      )}

      {/* ── Saved addresses step ── */}
      {step === 'saved' && auth?.sessionToken && (
        <SavedAddressStep
          sessionToken={auth.sessionToken}
          functionBaseUrl={supabaseFunctionBaseUrl}
          merchantId={merchantId}
          onSelectAddress={handleSelectSavedAddress}
          onAddNew={() => {
            setVerifyAddress(null);
            setConfirmedLocation(null);
            setStep('map');
          }}
        />
      )}

      {/* ── Verify address (lazy DigiPin) ── */}
      {step === 'verify-address' && verifyAddress && (
        <>
          <div className="qr-order-lock-banner" role="status">
            Confirm your delivery pin to complete this order
          </div>
          <MapPinSelector
            onLocationConfirm={handleLocationConfirm}
            defaultLat={verifyAddress.lat ?? defaultLat}
            defaultLng={verifyAddress.lng ?? defaultLng}
            mapHeight={mapHeight}
            theme={theme}
            indiaBoundaryUrl={indiaBoundaryUrl}
            enableSearch={enableSearch}
            apiKey={apiKey}
            apiBaseUrl={apiBaseUrl}
            brandColor={brandColor}
            mapStyle={mapStyle}
            embedMode={embedMode}
          />
        </>
      )}

      {/* ── Map step ── */}
      {step === 'map' && (
        <MapPinSelector
          onLocationConfirm={handleLocationConfirm}
          defaultLat={defaultLat}
          defaultLng={defaultLng}
          mapHeight={mapHeight}
          theme={theme}
          indiaBoundaryUrl={indiaBoundaryUrl}
          enableSearch={enableSearch}
          apiKey={apiKey}
          apiBaseUrl={apiBaseUrl}
          brandColor={brandColor}
          mapStyle={mapStyle}
          embedMode={embedMode}
        />
      )}

      {/* ── Form step ── */}
      {step === 'form' && confirmedLocation && (
        <AddressForm
          digipin={confirmedLocation.digipin}
          lat={confirmedLocation.lat}
          lng={confirmedLocation.lng}
          apiKey={apiKey}
          apiBaseUrl={apiBaseUrl}
          onAddressComplete={handleAddressComplete}
          onBack={handleBack}
          onError={onError}
          theme={theme}
          checkoutConfig={checkoutConfig}
          serviceabilitySessionToken={codSession}
          enableServiceabilityCheck={enableServiceabilityCheck}
          showIndiaPostDeliveryWarning={showIndiaPostDeliveryWarning}
          serviceabilityBlockCheckout={serviceabilityBlockCheckout}
          initialValues={
            verifyAddress
              ? {
                  flatNumber: verifyAddress.flat_number,
                  floorNumber: verifyAddress.floor_number,
                  buildingName: verifyAddress.building_name,
                  streetName: verifyAddress.street_name,
                  locality: verifyAddress.locality,
                  label: (verifyAddress.label === 'work' || verifyAddress.label === 'other'
                    ? verifyAddress.label
                    : 'home') as CompleteAddress['label'],
                  labelText: verifyAddress.label_text?.trim() || undefined,
                }
              : undefined
          }
          submitLabel={verifyAddress ? 'Confirm delivery pin' : undefined}
        />
      )}

      {/* ── Payment step ── */}
      {step === 'payment' && withPayment && codSession && paymentAmountPaise && (
        <PaymentStep
          checkoutConfig={checkoutConfig}
          sessionToken={codSession}
          sessionId={auth?.sessionId}
          amountPaise={paymentAmountPaise}
          enableCod={enableCod}
          codAllowed={codAllowed && !addressRisk?.interventions.includes('hide_cod')}
          addressRisk={addressRisk}
          theme={theme}
          onComplete={handlePaymentComplete}
          onBack={handleBack}
          onEvent={(type, paymentMethod) =>
            emit({ type, step: 'payment', paymentMethod, buyerId: auth?.buyerId })
          }
          orderConfirmationUrl={orderConfirmationUrl}
        />
      )}

      {/* ── Done ── */}
      {step === 'done' && completedAddress && (
        <>
          <SuccessScreen address={completedAddress} onEditAddress={handleEditAddress} />
          {paymentMethod ? (
            <p className="qr-cod-banner__text qr-cod-banner__text--ok" style={{ textAlign: 'center', margin: '8px 16px' }}>
              Paid via {paymentMethod === 'cod' ? 'Cash on Delivery' : 'online payment'}
            </p>
          ) : null}
          {enableCod && merchantId && supabaseFunctionBaseUrl && codSession ? (
            <>
              <CodAvailabilityBanner
                checkoutConfig={checkoutConfig}
                sessionToken={codSession}
                pincode={completedAddress.pincode}
                cartTotalPaise={codCartTotalPaise ?? paymentAmountPaise}
                theme={theme}
                onCodAllowedChange={setCodAllowed}
              />
              {enablePrepaidNudge && codAllowed ? (
                <PrepaidNudge
                  copy={prepaidNudgeCopy ?? 'Save more when you pay online — faster delivery & exclusive offers.'}
                  ctaUrl={prepaidNudgeCtaUrl}
                  theme={theme}
                />
              ) : null}
            </>
          ) : null}
        </>
      )}

      {/* Powered-by footer */}
      <div className="qr-footer">
        <span>Powered by</span>
        <a
          href="https://quantaroute.com"
          target="_blank"
          rel="noopener noreferrer"
          className="qr-footer__link"
        >
          QuantaRoute
        </a>
        <span className="qr-footer__flag" aria-label="Made in India">🇮🇳</span>
      </div>
    </div>
  );
};

export default CheckoutWidget;
