// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading;
using ManagedCommon;

namespace Microsoft.CmdPal.Ext.Calc.Helper;

internal sealed class HistoryStore
{
    private readonly string _filePath;
    private readonly List<HistoryItem> _items = [];
    private readonly Lock _lock = new();

    private int _capacity;

    public event EventHandler Changed;

    public HistoryStore(string filePath, int capacity)
    {
        ArgumentNullException.ThrowIfNull(filePath);
        ArgumentOutOfRangeException.ThrowIfNegative(capacity);

        _filePath = filePath;
        _capacity = capacity;

        _items.AddRange(LoadFromDiskSafe());
        TrimNoLock();
    }

    public IReadOnlyList<HistoryItem> HistoryItems
    {
        get
        {
            lock (_lock)
            {
                return [.. _items];
            }
        }
    }

    public void Add(HistoryItem item)
    {
        ArgumentNullException.ThrowIfNull(item);

        lock (_lock)
        {
            _items.Add(item);
            _ = TrimNoLock();
            SaveNoLock();
        }

        Changed?.Invoke(this, EventArgs.Empty);
    }

    public bool Remove(Guid id)
    {
        var removed = false;
        lock (_lock)
        {
            var index = _items.FindIndex(item => item.Id == id);
            if (index >= 0)
            {
                _items.RemoveAt(index);
                SaveNoLock();
                removed = true;
            }
        }

        if (removed)
        {
            Changed?.Invoke(this, EventArgs.Empty);
        }

        return removed;
    }

    public bool Clear()
    {
        var cleared = false;
        lock (_lock)
        {
            if (_items.Count > 0)
            {
                _items.Clear();
                SaveNoLock();
                cleared = true;
            }
        }

        if (cleared)
        {
            Changed?.Invoke(this, EventArgs.Empty);
        }

        return cleared;
    }

    public void SetCapacity(int capacity)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(capacity);

        bool trimmed;
        lock (_lock)
        {
            _capacity = capacity;
            trimmed = TrimNoLock();
            if (trimmed)
            {
                SaveNoLock();
            }
        }

        if (trimmed)
        {
            Changed?.Invoke(this, EventArgs.Empty);
        }
    }

    private bool TrimNoLock()
    {
        var max = _capacity;
        if (_items.Count > max)
        {
            _items.RemoveRange(0, _items.Count - max);
            return true;
        }

        return false;
    }

    private List<HistoryItem> LoadFromDiskSafe()
    {
        try
        {
            if (!File.Exists(_filePath))
            {
                return [];
            }

            var fileContent = File.ReadAllText(_filePath);
            var historyItems = JsonSerializer.Deserialize<List<HistoryItem>>(fileContent, CalculatorJsonSerializationContext.Default.HistoryItemList) ?? [];
            return historyItems;
        }
        catch (Exception ex)
        {
            Logger.LogError("Unable to load calculator history", ex);
            return [];
        }
    }

    private void SaveNoLock()
    {
        var json = JsonSerializer.Serialize(_items, CalculatorJsonSerializationContext.Default.HistoryItemList);
        File.WriteAllText(_filePath, json);
    }
}
