// ignore_for_file: public_member_api_docs, sort_constructors_first
// 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 'dart:io';

import 'package:path/path.dart';
import 'package:yaml/yaml.dart';

class IgnoreList {
  final List<String> ignoredFiles;
  final List<String> ignoredFolders;

  IgnoreList({
    required this.ignoredFiles,
    required this.ignoredFolders,
  });

  /// Only parse files an folders (no glob)
  /// - file            <- file
  /// - folder/         <- folder, ends with "/"
  /// - infolder/file   <- file
  /// - sub/folder/     <- folder, ends with "/"
  factory IgnoreList.fromConfig(YamlList? list) {
    final List<String> ignoredFiles = [];
    final List<String> ignoredFolders = [];

    final ignored =
        list?.map<String>((element) => element as String).toList() ?? [];

    for (final element in ignored) {
      if (element.endsWith('/')) {
        // This is a folder
        ignoredFolders.add(element.substring(0, element.length - 1));
      } else {
        // This is a file
        ignoredFiles.add(element);
      }
    }

    return IgnoreList(
      ignoredFiles: ignoredFiles,
      ignoredFolders: ignoredFolders,
    );
  }

  factory IgnoreList.fromBrickIgnoreFile() {
    throw UnimplementedError('.brickignore is not yet supported');
  }

  bool contains(String root, String path) {
    final FileSystemEntityType type = FileSystemEntity.typeSync(path);

    // Check if file/folder is an ignored folder (or any subtree of it)
    for (final element in ignoredFolders) {
      final ignoredPath = join(root, element);
      if (path.startsWith(ignoredPath)) {
        return true;
      }
    }

    switch (type) {
      case FileSystemEntityType.file:
        for (final element in ignoredFiles) {
          final ignoredPath = join(root, element);
          if (ignoredPath == path) {
            return true;
          }
        }
        return false;
      case FileSystemEntityType.directory:
        for (final element in ignoredFolders) {
          final ignoredPath = join(root, element);
          if (ignoredPath == path) {
            return true;
          }
        }
        return false;
      case FileSystemEntityType.link:
      case FileSystemEntityType.notFound:
      case FileSystemEntityType.pipe:
      case FileSystemEntityType.unixDomainSock:
        throw ArgumentError(
          'This file system entity type is not supported',
          'from',
        );
    }

    return false;
  }

  @override
  String toString() => 'IgnoreList(ignoredFiles: $ignoredFiles, '
      'ignoredFolders: $ignoredFolders)';
}
