// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import 'package:flutter/material.dart';
import 'package:wyatt_ui_components/src/core/utils/theme_helper.dart';
import 'package:wyatt_ui_components/src/domain/entities/theme_style.dart';

/// {@template theme_resolver}
/// In charge of theme negotiation and merge.
///
/// When you build a component `Component({double? radius})`.
/// You have several possibilities:
/// 1) Pass the "radius" into the constructor, `Component(radius: 12)`.
/// 2) Set up a theme extension `ComponentThemeExtension(radius: 15)`.
/// 3) Let `wyatt_ui_kit` "negotiate" and try to find a suitable style in the
/// flutter theme.
///
/// If this negotiation phase fails, then:
/// - If the value is mandatory: a hardcoded value in "wyatt_ui_kit" is chosen.
/// - If not, the style is simply not applied.
/// {@endtemplate}
abstract class ThemeResolver<S extends ThemeStyle<S>, T, E> {
  /// {@macro theme_resolver}
  const ThemeResolver();

  S? Function(BuildContext context, {E? extra}) get customStyleFn;

  /// Compute default value from Flutter Theme or with hardcoded values.
  S computeDefaultValue(
    BuildContext context, {
    E? extra,
  });

  /// Compute extension value from custom component extension.
  S? computeExtensionValueFn(
    BuildContext context,
    T? themeExtension, {
    E? extra,
  });

  /// Choose most suitable style for a given context.
  S negotiate(BuildContext context, {E? extra}) {
    final style = computeDefaultValue(context, extra: extra);
    return ThemeHelper.getThemeElement<S, S>(
          [
            style,
            computeExtensionValueFn(
              context,
              Theme.of(context).extension<T>(),
              extra: extra,
            ),
            customStyleFn(context, extra: extra)
          ],
          transform: (value) => value,
          combine: (value, element) => value?.mergeWith(element),
        ) ??
        style;
  }
}
