// Copyright 2019 Google Inc. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:source_span/source_span.dart';

import 'ast/css.dart';
import 'ast/node.dart';
import 'callable.dart';
import 'extend/extension_store.dart';
import 'value.dart';

/// The interface for a Sass module.
abstract interface class Module<T extends AsyncCallable> {
  /// The canonical URL for this module's source file.
  ///
  /// This may be `null` if the module was loaded from a string without a URL
  /// provided.
  Uri? get url;

  /// Modules that this module uses.
  List<Module<T>> get upstream;

  /// The module's variables.
  Map<String, Value> get variables;

  /// The nodes where each variable in [_variables] was defined.
  ///
  /// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
  /// [AstNode.span] if the span isn't required, since some nodes need to do
  /// real work to manufacture a source span.
  ///
  /// Implementations must ensure that this has the same keys as [variables].
  Map<String, AstNode> get variableNodes;

  /// The module's functions.
  ///
  /// Implementations must ensure that each [AsyncCallable] is stored under its
  /// own name.
  Map<String, T> get functions;

  /// The module's mixins.
  ///
  /// Implementations must ensure that each [AsyncCallable] is stored under its
  /// own name.
  Map<String, T> get mixins;

  /// The extensions defined in this module, which is also able to update
  /// [css]'s style rules in-place based on downstream extensions.
  ExtensionStore get extensionStore;

  /// The module's CSS tree.
  CssStylesheet get css;

  /// A map from modules in [upstream] to loud comments written in this module
  /// that should be emitted before the given module.
  Map<Module<T>, List<CssComment>> get preModuleComments;

  /// Whether this module *or* any modules in [upstream] contain any CSS.
  bool get transitivelyContainsCss;

  /// Whether this module *or* any modules in [upstream] contain `@extend`
  /// rules.
  bool get transitivelyContainsExtensions;

  /// Sets the variable named [name] to [value], associated with
  /// [nodeWithSpan]'s source span.
  ///
  /// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling
  /// [AstNode.span] if the span isn't required, since some nodes need to do
  /// real work to manufacture a source span.
  ///
  /// Throws a [SassScriptException] if this module doesn't define a variable
  /// named [name].
  void setVariable(String name, Value value, AstNode nodeWithSpan);

  /// Returns an opaque object that will be equal to another
  /// `variableIdentity()` return value for the same name in another module if
  /// and only if both modules expose identical definitions of the variable in
  /// question, as defined by the Sass spec.
  Object variableIdentity(String name);

  /// Whether this module exposes any variables from among [variables] that
  /// could have been configured when the module was loaded.
  bool couldHaveBeenConfigured(Set<String> variables);

  /// Creates a copy of this module with new [css] and [extender].
  Module<T> cloneCss();
}
