// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text.Json;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

#nullable enable

internal sealed class FileCache
{
    private CompilerCache? _newCache;
    private CompilerCache? _oldCache;
    private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions { WriteIndented = true };

    public bool Enabled { get; }
    public TaskLoggingHelper Log { get; }

    public FileCache(string? cacheFilePath, TaskLoggingHelper log)
    {
        Log = log;
        if (string.IsNullOrEmpty(cacheFilePath))
        {
            Log.LogMessage(MessageImportance.Low, $"Disabling cache, because CacheFilePath is not set");
            return;
        }

        Enabled = true;
        if (File.Exists(cacheFilePath))
        {
            _oldCache = JsonSerializer.Deserialize<CompilerCache>(File.ReadAllText(cacheFilePath), s_jsonOptions);
        }

        _oldCache ??= new();
        _newCache = new(_oldCache.FileHashes);
    }

    public bool UpdateAndCheckHasFileChanged(string filePath, string newHash)
    {
        if (!Enabled)
            throw new InvalidOperationException("Cache is not enabled. Make sure the cache file path is set");

        _newCache!.FileHashes[filePath] = newHash;
        return !_oldCache!.FileHashes.TryGetValue(filePath, out string? oldHash) || oldHash != newHash;
    }

    public bool ShouldCopy(ProxyFile proxyFile, [NotNullWhen(true)] out string? cause)
    {
        if (!Enabled)
            throw new InvalidOperationException("Cache is not enabled. Make sure the cache file path is set");

        cause = null;

        string newHash = Utils.ComputeHash(proxyFile.TempFile);
        _newCache!.FileHashes[proxyFile.TargetFile] = newHash;

        if (!File.Exists(proxyFile.TargetFile))
        {
            cause = $"the output file didn't exist";
            return true;
        }

        string? oldHash;
        if (!_oldCache!.FileHashes.TryGetValue(proxyFile.TargetFile, out oldHash))
            oldHash = Utils.ComputeHash(proxyFile.TargetFile);

        if (oldHash != newHash)
        {
            cause = $"hash for the file changed";
            return true;
        }

        return false;
    }

    public bool Save(string? cacheFilePath)
    {
        if (!Enabled || string.IsNullOrEmpty(cacheFilePath))
            return false;

        var json = JsonSerializer.Serialize (_newCache, s_jsonOptions);
        File.WriteAllText(cacheFilePath!, json);
        return true;
    }

    public ProxyFile NewFile(string targetFile) => new ProxyFile(targetFile, this);
}
