// Copyright 2016 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:meta/meta.dart';
import 'package:source_span/source_span.dart';

import '../../../exception.dart';
import '../../../parse/scss.dart';
import '../../../util/span.dart';
import '../../../visitor/interface/statement.dart';
import '../configured_variable.dart';
import '../dependency.dart';
import '../expression/string.dart';
import '../statement.dart';

/// A `@use` rule.
///
/// {@category AST}
final class UseRule extends Statement implements SassDependency {
  /// The URI of the module to use.
  ///
  /// If this is relative, it's relative to the containing file.
  final Uri url;

  /// The namespace for members of the used module, or `null` if the members
  /// can be accessed without a namespace.
  final String? namespace;

  /// A list of variable assignments used to configure the loaded modules.
  final List<ConfiguredVariable> configuration;

  final FileSpan span;

  FileSpan get urlSpan => span.withoutInitialAtRule().initialQuoted();

  UseRule(
    this.url,
    this.namespace,
    this.span, {
    Iterable<ConfiguredVariable>? configuration,
  }) : configuration = configuration == null
            ? const []
            : List<ConfiguredVariable>.unmodifiable(configuration) {
    for (var variable in this.configuration) {
      if (variable.isGuarded) {
        throw ArgumentError.value(
          variable,
          "configured variable",
          "can't be guarded in a @use rule.",
        );
      }
    }
  }

  /// Parses a `@use` rule from [contents].
  ///
  /// If passed, [url] is the name of the file from which [contents] comes.
  ///
  /// Throws a [SassFormatException] if parsing fails.
  ///
  /// @nodoc
  @internal
  factory UseRule.parse(String contents, {Object? url}) =>
      ScssParser(contents, url: url).parseUseRule().$1;

  T accept<T>(StatementVisitor<T> visitor) => visitor.visitUseRule(this);

  String toString() {
    var buffer = StringBuffer(
      "@use ${StringExpression.quoteText(url.toString())}",
    );

    var basename = url.pathSegments.isEmpty ? "" : url.pathSegments.last;
    var dot = basename.indexOf(".");
    if (namespace != basename.substring(0, dot == -1 ? basename.length : dot)) {
      buffer.write(" as ${namespace ?? "*"}");
    }

    if (configuration.isNotEmpty) {
      buffer.write(" with (${configuration.join(", ")})");
    }

    buffer.write(";");
    return buffer.toString();
  }
}
