// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart'
    show AnalysisContextCollectionImpl;
import 'package:path/path.dart' as path;
import 'package:test_descriptor/test_descriptor.dart' as d;

import '../../tool/mustachio/builder.dart';
import '../src/test_descriptor_utils.dart';

export '../../tool/mustachio/builder.dart';

/// The build package Asset for a copy of the Renderer annotation for tests.
///
/// In testing builders, the builder cannot access both in-line assets specified
/// in tests, _and_ assets on disk in the package.
// Update this when [Renderer] and [Context] are updated.
const annotationsContent = '''
class Renderer {
  final Symbol name;

  final Context context;

  final Set<Type> visibleTypes;

  final String standardHtmlTemplate;

  final String standardMdTemplate;

  const Renderer(
    this.name,
    this.context,
    String standardTemplateBasename, {
    this.visibleTypes = const {},
  })  : standardHtmlTemplate = 'lib/templates/\${standardTemplateBasename}.html';
}

class Context<T> {
  const Context();
}
''';

/// Front matter for a library which declares a Renderer for `Foo`.
const libraryFrontMatter = '''
@Renderer(#renderFoo, Context<Foo>(), 'foo', visibleTypes: {Bar, Baz})
library foo;
import 'annotations.dart';
''';

/// Tests the Mustachio builder using in-memory Assets.
Future<void> testMustachioBuilder(
  String sourceLibraryContent, {
  String libraryFrontMatter = libraryFrontMatter,
  Iterable<DirectoryDescriptor> Function()? additionalAssets,
}) async {
  sourceLibraryContent = '''
$libraryFrontMatter
$sourceLibraryContent
''';
  additionalAssets ??= () => [];
  await d.dir('foo_package', [
    d.dir('lib', [
      d.file('annotations.dart', annotationsContent),
      d.file('foo.dart', sourceLibraryContent),
      d.dir('templates', [
        d.file('foo.html', 's1 is {{ s1 }}'),
        d.file('bar.html', 'EMPTY'),
        d.file('baz.html', 'EMPTY'),
      ]),
    ]),
  ]).create();
  await d.dir('foo_package', [...additionalAssets()]).create();
  await build(path.join(d.sandbox, 'foo_package', 'lib/foo.dart'),
      root: path.join(d.sandbox, 'foo_package'));
}

Future<LibraryElement> resolveGeneratedLibrary2(String libraryPath) async {
  var contextCollection = AnalysisContextCollectionImpl(
    includedPaths: [d.sandbox],
    // TODO(jcollins-g): should we pass excluded directories here instead of
    // handling it ourselves?
    resourceProvider: PhysicalResourceProvider.INSTANCE,
    sdkPath: sdkPath,
    withFineDependencies: true,
  );
  var analysisContext = contextCollection.contextFor(d.sandbox);
  final libraryResult =
      await analysisContext.currentSession.getResolvedLibrary(libraryPath);
  if (libraryResult is! ResolvedLibraryResult) {
    throw StateError(
        'Expected library result to be ResolvedLibraryResult, but is '
        '${libraryResult.runtimeType}');
  }

  return libraryResult.element;
}
