import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import clsx from 'clsx';
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
import {
  useSetShippingOptionMutation,
  useSetAttributeBasedShippingOptionsMutation,
  useSetDataSourceShippingOptionsMutation
} from '@akinon/next/data/client/checkout';
import { setSelectedShippingOptions } from '@akinon/next/redux/reducers/checkout';
import { useLocalization } from '@akinon/next/hooks';
import { Button, Price, Radio } from '@theme/components';
import type { RootState } from '@theme/redux/store';
import AccordionSection, { SectionStatus } from '../accordion-section';
import { ShippingOptionsSkeleton } from '../skeletons/shipping-skeleton';

interface ShippingSectionProps {
  status: SectionStatus;
  onEdit: () => void;
  onComplete: () => void;
  editable?: boolean;
}

const ShippingSection = ({
  status,
  onEdit,
  onComplete,
  editable = true
}: ShippingSectionProps) => {
  const { t } = useLocalization();
  const dispatch = useAppDispatch();
  const {
    shippingOptions,
    attributeBasedShippingOptions,
    dataSourceShippingOptions,
    preOrder,
    selectedShippingOptions
  } = useAppSelector((state: RootState) => state.checkout);
  const {
    shipping_option,
    attribute_based_shipping_options,
    data_source_shipping_options
  } = preOrder ?? {};

  const [setShippingOption, { isLoading: isSettingPlain }] =
    useSetShippingOptionMutation();
  const [setAttributeBasedShippingOptions, { isLoading: isSettingAttribute }] =
    useSetAttributeBasedShippingOptionsMutation();
  const [setDataSourceShippingOption, { isLoading: isSettingDataSource }] =
    useSetDataSourceShippingOptionsMutation();
  const isSetting = isSettingPlain || isSettingAttribute || isSettingDataSource;

  const prevAttributeBasedRef = useRef(attributeBasedShippingOptions);
  const [selectedDataSourcePks, setSelectedDataSourcePks] = useState<
    { dataSourcePk: number; optionPk: number }[] | null
  >(null);

  const initializeAttributeOptions = useCallback(() => {
    if (!attribute_based_shipping_options) return;
    const next = { ...selectedShippingOptions };
    let changed = false;
    Object.entries(attributeBasedShippingOptions).forEach(([key, options]) => {
      if (!next[key] || !options.some((opt) => opt.pk === next[key])) {
        next[key] = options[0].pk;
        changed = true;
      }
    });
    if (changed) {
      dispatch(setSelectedShippingOptions(next));
      setAttributeBasedShippingOptions(next);
    }
  }, [
    attributeBasedShippingOptions,
    selectedShippingOptions,
    attribute_based_shipping_options,
    dispatch,
    setAttributeBasedShippingOptions
  ]);

  useEffect(() => {
    if (!data_source_shipping_options) return;
    setSelectedDataSourcePks(
      data_source_shipping_options.map((option) => ({
        dataSourcePk: option.data_source.pk,
        optionPk: option.pk
      }))
    );
  }, [data_source_shipping_options]);

  useEffect(() => {
    if (
      JSON.stringify(prevAttributeBasedRef.current) !==
        JSON.stringify(attributeBasedShippingOptions) ||
      Object.keys(selectedShippingOptions).length === 0
    ) {
      initializeAttributeOptions();
      prevAttributeBasedRef.current = attributeBasedShippingOptions;
    }
  }, [
    attributeBasedShippingOptions,
    selectedShippingOptions,
    initializeAttributeOptions
  ]);

  const handlePlainOption = (pk: number) => {
    setShippingOption(pk);
  };

  const handleAttributeOption = (key: string, pk: number) => {
    const next = { ...selectedShippingOptions, [key]: pk };
    dispatch(setSelectedShippingOptions(next));
    setAttributeBasedShippingOptions(next);
  };

  const handleDataSourceOption = (dataSourcePk: number, optionPk: number) => {
    const updated = selectedDataSourcePks?.map((item) =>
      item.dataSourcePk === dataSourcePk ? { ...item, optionPk } : item
    );
    if (!updated) return;
    setSelectedDataSourcePks(updated);
    setDataSourceShippingOption(updated.map((item) => item.optionPk));
  };

  const summary = shipping_option ? (
    <div className="flex flex-col">
      <span className="font-medium">{shipping_option.name}</span>
      {shipping_option.shipping_amount && (
        <span className="text-xs text-black-700">
          <Price value={shipping_option.shipping_amount} />
        </span>
      )}
    </div>
  ) : null;

  const hasAttributeOptions = useMemo(
    () =>
      attributeBasedShippingOptions &&
      Object.keys(attributeBasedShippingOptions).length > 0,
    [attributeBasedShippingOptions]
  );

  const hasDataSourceOptions = useMemo(
    () => dataSourceShippingOptions && dataSourceShippingOptions.length > 0,
    [dataSourceShippingOptions]
  );

  const noOptionsLoaded =
    shippingOptions.length === 0 &&
    !hasAttributeOptions &&
    !hasDataSourceOptions;

  return (
    <AccordionSection
      step={3}
      title={t('checkout.one_page.shipping.title')}
      status={status}
      onEdit={onEdit}
      editable={editable}
      summary={summary}
      dataTestId="one-page-shipping"
    >
      {noOptionsLoaded ? (
        <ShippingOptionsSkeleton />
      ) : (
        <div
          className={clsx('flex flex-col gap-4', {
            'opacity-60 pointer-events-none': isSetting
          })}
        >
          {shippingOptions.length > 0 && (
            <div
              className="flex flex-col gap-2"
              role="radiogroup"
              aria-label={t('checkout.one_page.shipping.title')}
            >
              {shippingOptions.map((option) => (
                <label
                  key={option.pk}
                  className={clsx(
                    'flex cursor-pointer items-center justify-between rounded-sm border px-4 py-3 transition-all',
                    'focus-within:ring-2 focus-within:ring-secondary focus-within:ring-offset-1',
                    shipping_option?.pk === option.pk
                      ? 'border-secondary-400 bg-secondary-50/50'
                      : 'border-gray-300 hover:border-secondary-400'
                  )}
                >
                  <Radio
                    name="one-page-shipping"
                    checked={option.pk === shipping_option?.pk}
                    onChange={() => handlePlainOption(option.pk)}
                    data-testid={`one-page-shipping-option-${option.pk}`}
                  >
                    <span className="ml-1 text-sm">{option.name}</span>
                  </Radio>
                  <span className="text-sm font-medium">
                    <Price value={option.shipping_amount} />
                  </span>
                </label>
              ))}
            </div>
          )}

          {hasAttributeOptions &&
            Object.entries(attributeBasedShippingOptions).map(
              ([key, options]) => (
                <div key={key}>
                  <h4 className="mb-2 text-sm font-medium text-black-800">
                    {key}
                  </h4>
                  <div
                    className="flex flex-col gap-2"
                    role="radiogroup"
                    aria-label={key}
                  >
                    {options.map((option) => (
                      <label
                        key={option.pk}
                        className="flex cursor-pointer items-center justify-between rounded-sm border border-gray-300 px-4 py-3 hover:border-secondary-400"
                      >
                        <Radio
                          name={`one-page-attr-${key}`}
                          checked={selectedShippingOptions[key] === option.pk}
                          onChange={() =>
                            handleAttributeOption(key, option.pk)
                          }
                          data-testid={`one-page-attr-shipping-${option.pk}`}
                        >
                          <span className="ml-1 text-sm">
                            {option.shipping_option_name}
                          </span>
                        </Radio>
                        <span className="text-sm font-medium">
                          <Price value={option.shipping_amount} />
                        </span>
                      </label>
                    ))}
                  </div>
                </div>
              )
            )}

          {hasDataSourceOptions &&
            dataSourceShippingOptions.map((source) => (
              <div key={source.pk}>
                <h4 className="mb-2 text-sm font-medium text-black-800">
                  {source.name}
                </h4>
                <div
                  className="flex flex-col gap-2"
                  role="radiogroup"
                  aria-label={source.name}
                >
                  {source.data_source_shipping_options.map((opt) => (
                    <label
                      key={opt.pk}
                      className="flex cursor-pointer items-center justify-between rounded-sm border border-gray-300 px-4 py-3 hover:border-secondary-400"
                    >
                      <Radio
                        name={`one-page-ds-${source.pk}`}
                        checked={
                          selectedDataSourcePks?.some(
                            (item) => item.optionPk === opt.pk
                          ) || false
                        }
                        onChange={() =>
                          handleDataSourceOption(source.pk, opt.pk)
                        }
                        data-testid={`one-page-ds-shipping-${opt.pk}`}
                      >
                        <span className="ml-1 text-sm">
                          {opt.shipping_option_name}
                        </span>
                      </Radio>
                      <span className="text-sm font-medium">
                        <Price value={opt.shipping_amount} />
                      </span>
                    </label>
                  ))}
                </div>
              </div>
            ))}

          {shipping_option && (
            <div className="flex justify-end">
              <Button
                onClick={onComplete}
                data-testid="one-page-shipping-continue"
              >
                {t('checkout.one_page.continue_to_payment')}
              </Button>
            </div>
          )}
        </div>
      )}
    </AccordionSection>
  );
};

export default ShippingSection;
