// Copyright (c) 2014, 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:path/path.dart' as path;

/// Configures where output of the protoc compiler should be placed and how to
/// import one generated file from another.
abstract class OutputConfiguration {
  const OutputConfiguration();

  /// Resolves an import of a generated Dart file.
  ///
  /// Both [source] and [target] are .proto files, where [source] imports
  /// [target].
  ///
  /// The returned URI can be used within one of the source's dart files to
  /// import the target's generated file with the given extension.
  Uri resolveImport(Uri target, Uri source, String extension);

  /// Returns the path where the .pb.dart file will be placed.
  ///
  /// The input is a .proto file and the output is a path under the output
  /// folder.
  Uri outputPathFor(Uri inputPath, String extension);
}

/// Default [OutputConfiguration] that uses the same path as the input
/// file for the output file (just replaces the extension), and that uses
/// relative paths to resolve imports.
class DefaultOutputConfiguration extends OutputConfiguration {
  const DefaultOutputConfiguration();

  @override
  Uri outputPathFor(Uri inputPath, String extension) {
    final base = path.withoutExtension(path.url.fromUri(inputPath));
    return path.url.toUri('$base$extension');
  }

  @override
  Uri resolveImport(Uri target, Uri source, String extension) {
    assert(extension.startsWith('.'));
    final targetPath = path.url.fromUri(target);
    final wellKnownImport = _wellKnownProtoImport(targetPath, extension);
    if (wellKnownImport != null) {
      return path.url.toUri(wellKnownImport);
    }
    final sourceDir = path.url.dirname(path.url.fromUri(source));
    final base = path.withoutExtension(
      path.url.relative(targetPath, from: sourceDir),
    );
    return path.url.toUri('$base$extension');
  }
}

const _wktImportPrefix = 'google/protobuf/';

Set<String> _wellKnownTypeProtoPaths = {
  '${_wktImportPrefix}any.proto',
  '${_wktImportPrefix}api.proto',
  '${_wktImportPrefix}duration.proto',
  '${_wktImportPrefix}empty.proto',
  '${_wktImportPrefix}field_mask.proto',
  '${_wktImportPrefix}source_context.proto',
  '${_wktImportPrefix}struct.proto',
  '${_wktImportPrefix}timestamp.proto',
  '${_wktImportPrefix}type.proto',
  '${_wktImportPrefix}wrappers.proto',
};

String? _wellKnownProtoImport(String importPath, String extension) {
  if (!_wellKnownTypeProtoPaths.contains(importPath)) {
    return null;
  }
  final importPathWithoutExtension = path.withoutExtension(importPath);
  return 'package:protobuf/well_known_types/$importPathWithoutExtension$extension';
}
