using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEngine;

/// <summary>
/// Audits which assets from third-party packages are actually referenced by the build.
/// Traces the full reference chain from both build scenes AND Addressable asset groups.
/// Results are logged to the console and written to a CSV file.
///
/// Usage: Tools > Audit Asset Usage
/// </summary>
public static class AssetUsageAuditor {
    // Third-party asset pack folders to audit.
    // Add/remove entries as needed. Paths are relative to Assets/.
    private static readonly string[] PacksToAudit = {
        "4000_Fantasy_Icons",
        "AfricaAnimalsPackLowPoly V2",
        "AfricaAnimalsPackv1",
        "Animal pack deluxe",
        "Animal pack deluxe v2",
        "AustraliaAnimalsPackv1",
        "Clown.fat",
        "DinoPackLowPolyV1",
        "Dragon",
        "DungeonMonsters2D",
        "EffectExamples",
        "Fantasy Interface Sounds",
        "GUI Pro Kit Fantasy RPG",
        "HEROIC FANTASY CREATURES FULL PACK Vol 2",
        "Hex Tiles",
        "HoneyBadger",
        "Magic Spells Sound Effects LITE",
        "Medieval Combat Sounds",
        "Modern UI Pack",
        "Music",
        "My Production Line",
        "Polytope Studio",
        "Raccoon",
        "RRFreelance-Characters",
        "StrategyGameIcons",
        "Stylize Water Texture",
        "Terrain Hexes",
        "TileableBridgePack",
    };

    [MenuItem("Tools/Audit Asset Usage")]
    public static void AuditAssetUsage() {
        Debug.Log("=== Asset Usage Audit: Starting ===");

        // Collect all assets referenced by enabled build scenes
        var scenePaths =
                EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();

        if (scenePaths.Length == 0) {
            Debug.LogError("No enabled scenes in Build Settings. Cannot audit.");
            return;
        }

        Debug.Log($"Tracing dependencies from {scenePaths.Length} build scenes...");
        var referencedAssets =
                new HashSet<string>(AssetDatabase.GetDependencies(scenePaths, recursive: true));
        Debug.Log($"Found {referencedAssets.Count} referenced assets from scenes.");

        // Also trace dependencies from all Addressable asset group entries
        var addressableSettings = AddressableAssetSettingsDefaultObject.Settings;
        if (addressableSettings != null) {
            var addressablePaths = new List<string>();
            foreach (var group in addressableSettings.groups) {
                if (group == null) continue;
                foreach (var entry in group.entries) { addressablePaths.Add(entry.AssetPath); }
            }

            Debug.Log($"Tracing dependencies from {addressablePaths.Count} Addressable entries...");
            var addressableDeps =
                    AssetDatabase.GetDependencies(addressablePaths.ToArray(), recursive: true);
            foreach (var dep in addressableDeps) { referencedAssets.Add(dep); }

            Debug.Log(
                    $"Found {referencedAssets.Count} total referenced assets (scenes + Addressables).");
        } else {
            Debug.LogWarning("Addressable settings not found — audit only covers build scenes.");
        }

        var csvLines = new List<string> {
            "Pack,Total Files,Used Files,Unused Files,Total MB,Used MB,Unused MB"
        };
        var detailCsvLines = new List<string> { "Pack,State,Size MB,Path" };
        long grandTotalUsed = 0;
        long grandTotalUnused = 0;

        foreach (var pack in PacksToAudit) {
            var folder = $"Assets/{pack}";
            if (!AssetDatabase.IsValidFolder(folder)) {
                Debug.Log($"  SKIP {pack} (folder not found)");
                continue;
            }

            var guids = AssetDatabase.FindAssets("", new[] { folder });
            long usedBytes = 0;
            long unusedBytes = 0;
            int usedCount = 0;
            int unusedCount = 0;
            var unusedLargeFiles = new List<(string path, long size)>();
            var usedFiles = new List<(string path, long size)>();

            foreach (var guid in guids) {
                var path = AssetDatabase.GUIDToAssetPath(guid);
                if (AssetDatabase.IsValidFolder(path)) continue;

                var fullPath = Path.Combine(Application.dataPath, "..", path);
                if (!File.Exists(fullPath)) continue;

                var size = new FileInfo(fullPath).Length;

                if (referencedAssets.Contains(path)) {
                    usedBytes += size;
                    usedCount++;
                    usedFiles.Add((path, size));
                    detailCsvLines.Add($"{Csv(pack)},Used,{size / (1024f * 1024f):F3},{Csv(path)}");
                } else {
                    unusedBytes += size;
                    unusedCount++;
                    if (size > 1024 * 1024) unusedLargeFiles.Add((path, size));
                    detailCsvLines.Add(
                            $"{Csv(pack)},Unused,{size / (1024f * 1024f):F3},{Csv(path)}");
                }
            }

            grandTotalUsed += usedBytes;
            grandTotalUnused += unusedBytes;

            var totalMB = (usedBytes + unusedBytes) / (1024f * 1024f);
            var usedMB = usedBytes / (1024f * 1024f);
            var unusedMB = unusedBytes / (1024f * 1024f);

            Debug.Log(
                    $"  {pack}: {usedCount + unusedCount} files ({totalMB:F1} MB) — " +
                    $"{usedCount} used ({usedMB:F1} MB), {unusedCount} unused ({unusedMB:F1} MB)");

            // Log all used files
            foreach (var (path, size) in usedFiles)
                Debug.Log($"    USED ({size / (1024f * 1024f):F2} MB): {path}");

            // Log the largest unused files
            unusedLargeFiles.Sort((a, b) => b.size.CompareTo(a.size));
            foreach (var (path, size) in unusedLargeFiles.Take(5))
                Debug.Log($"    UNUSED ({size / (1024f * 1024f):F1} MB): {path}");

            csvLines.Add(
                    $"{pack},{usedCount + unusedCount},{usedCount},{unusedCount}," +
                    $"{totalMB:F1},{usedMB:F1},{unusedMB:F1}");
        }

        Debug.Log(
                $"=== TOTAL: {grandTotalUsed / (1024f * 1024f):F1} MB used, " +
                $"{grandTotalUnused / (1024f * 1024f):F1} MB unused ===");

        // Write CSV
        var csvPath = Path.Combine(Application.dataPath, "..", "asset_usage_audit.csv");
        File.WriteAllLines(csvPath, csvLines);
        Debug.Log($"CSV written to: {csvPath}");

        var detailCsvPath = Path.Combine(Application.dataPath, "..", "asset_usage_audit_files.csv");
        File.WriteAllLines(detailCsvPath, detailCsvLines);
        Debug.Log($"Detail CSV written to: {detailCsvPath}");
    }

    private static string Csv(string value) {
        if (value.IndexOfAny(new[] { ',', '"', '\n', '\r' }) < 0) return value;
        return "\"" + value.Replace("\"", "\"\"") + "\"";
    }
}
