using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NUnit.Framework;
using UnityEngine;

namespace eagle0.Tests {
    [TestFixture]
    public class PostProcessingStackRemovalTests {
        private static readonly string[] ScanRoots = {
            "Assets",
            "Packages",
            "ProjectSettings",
        };

        private static readonly string[] IgnoredPathParts = {
            "/Assets/Editor/",
            "/Assets/Eagle/Editor/",
            "/Assets/Shardok/Editor/",
        };

        private static readonly string[] TextFileExtensions = {
            ".asmdef",
            ".asset",
            ".cs",
            ".json",
            ".mat",
            ".meta",
            ".prefab",
            ".shader",
            ".shadergraph",
            ".unity",
        };

        private static readonly string[] PostProcessingPatterns = {
            "UnityEngine.Rendering.PostProcessing",
            "PostProcessProfile",
            "PostProcessLayer",
            "PostProcessVolume",
            "com.unity.postprocessing",
        };

        [Test]
        public void ProductionAssetsDoNotReferenceLegacyPostProcessingStack() {
            var projectRoot = Directory.GetParent(Application.dataPath).FullName;
            var violations = new List<string>();

            foreach (var path in TextFilesToScan(projectRoot)) {
                var text = File.ReadAllText(path);
                if (!PostProcessingPatterns.Any(
                            pattern => text.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) >=
                                       0)) {
                    continue;
                }

                var lines = text.Split('\n');
                for (var i = 0; i < lines.Length; i++) {
                    foreach (var pattern in PostProcessingPatterns) {
                        if (lines[i].IndexOf(pattern, StringComparison.OrdinalIgnoreCase) < 0) {
                            continue;
                        }

                        violations.Add($"{RelativePath(projectRoot, path)}:{i + 1}: {pattern}");
                    }
                }
            }

            Assert.IsEmpty(
                    violations,
                    "Legacy Post Processing Stack v2 references should stay out of runtime assets and project settings:\n" +
                            string.Join("\n", violations));
        }

        private static IEnumerable<string> TextFilesToScan(string projectRoot) {
            return ScanRoots.Select(root => Path.Combine(projectRoot, root))
                    .Where(Directory.Exists)
                    .SelectMany(
                            root => Directory
                                            .EnumerateFiles(root, "*", SearchOption.AllDirectories))
                    .Where(path => !IsIgnoredPath(projectRoot, path))
                    .Where(path => TextFileExtensions.Contains(
                                   Path.GetExtension(path),
                                   StringComparer.OrdinalIgnoreCase))
                    .OrderBy(path => path, StringComparer.Ordinal);
        }

        private static bool IsIgnoredPath(string projectRoot, string path) {
            var relativePath = "/" + RelativePath(projectRoot, path);
            return IgnoredPathParts.Any(
                    ignoredPathPart =>
                            relativePath.Contains(ignoredPathPart, StringComparison.Ordinal));
        }

        private static string RelativePath(string projectRoot, string path) {
            return path.Substring(projectRoot.Length + 1).Replace(Path.DirectorySeparatorChar, '/');
        }
    }
}
