// Copyright (C) 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import './radio_group.scss';
import m from 'mithril';
import {classForIntent, type HTMLAttrs, Intent} from './common';
import {createContext} from '../base/mithril_utils';
import {classNames} from '../base/classnames';
import {Icon} from './icon';

// A group of mutually-exclusive buttons (like radio buttons).
//
// Supports both controlled and uncontrolled usage.
//
// Uncontrolled — the widget tracks its own selection state internally:
//
//   m(RadioGroup, {intent: Intent.Primary}, [
//     m(RadioGroup.Button, {value: 'day'}, 'Day'),
//     m(RadioGroup.Button, {value: 'week'}, 'Week'),
//     m(RadioGroup.Button, {value: 'month'}, 'Month'),
//   ]);
//
// Controlled — the parent owns the selected value and updates it in the
// callback:
//
//   m(RadioGroup, {
//     selectedValue: this.view,
//     onValueChange: (value) => (this.view = value),
//   }, [
//     m(RadioGroup.Button, {value: 'top-down', icon: 'arrow_downward'}, 'Top Down'),
//     m(RadioGroup.Button, {value: 'bottom-up', icon: 'arrow_upward'}, 'Bottom Up'),
//   ]);
export interface RadioGroupAttrs extends HTMLAttrs {
  readonly initialValue?: string;

  // The value of the selected button. Omit for uncontrolled mode.
  readonly selectedValue?: string;

  // Called when the user picks an option.
  readonly onValueChange?: (value: string) => void;

  // Disables all buttons when true.
  // false by default.
  readonly disabled?: boolean;

  // Whether the buttons should stretch to fill the container width.
  // false by default.
  readonly fillWidth?: boolean;

  // What color to make the selected button.
  readonly intent?: Intent;
}

interface RadioGroupState {
  readonly selectedValue?: string;
  readonly onValueChange?: (value: string) => void;
}

const {Consumer, Provider} = createContext<RadioGroupState>({});

export function RadioGroup({
  attrs,
}: m.Vnode<RadioGroupAttrs>): m.Component<RadioGroupAttrs> {
  let selectedValueInternal: string | undefined = attrs.initialValue;

  return {
    view({attrs, children}: m.Vnode<RadioGroupAttrs>) {
      const {
        disabled,
        fillWidth,
        intent = Intent.None,
        selectedValue = selectedValueInternal,
        onValueChange = (value: string) => (selectedValueInternal = value),
        ...htmlAttrs
      } = attrs;
      return m(
        '.pf-radio-group',
        {
          ...htmlAttrs,
          'role': 'radiogroup',
          'disabled': disabled ? true : undefined,
          'aria-disabled': disabled ? 'true' : undefined,
          'className': classNames(
            fillWidth && 'pf-radio-group--fill-width',
            classForIntent(intent),
          ),
        },
        m(Provider, {value: {selectedValue, onValueChange}}, children),
      );
    },
  };
}

export namespace RadioGroup {
  export interface ButtonAttrs extends HTMLAttrs {
    // Unique value for this button, used to identify the selected one.
    readonly value: string;

    // The icon of the button.
    readonly icon?: string;
  }

  export const Button: m.Component<ButtonAttrs> = {
    view({attrs, children}: m.CVnode<ButtonAttrs>) {
      const {value, icon, className, ...htmlAttrs} = attrs;
      return m(Consumer, ({selectedValue, onValueChange}) => {
        const isSelected = selectedValue === value;
        return m(
          '.pf-radio-group__button',
          {
            ...htmlAttrs,
            'role': 'radio',
            'aria-checked': isSelected ? 'true' : 'false',
            'onclick': () => onValueChange?.(value),
            'onkeydown': (e: KeyboardEvent) => {
              if (e.key === ' ' || e.key === 'Enter') {
                e.preventDefault();
                onValueChange?.(value);
              }
            },
            'className': classNames(className, isSelected && 'pf-active'),
          },
          icon && m(Icon, {icon}),
          children,
        );
      });
    },
  };
}
