using NubLang.Ast;
using NubLang.Diagnostics;
using NubLang.Modules;
using NubLang.Syntax;

namespace NubLang.LSP;

public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
{
    private record Unit(SyntaxTree SyntaxTree, DateTimeOffset FileTimestamp, List<Diagnostic> Diagnostics);

    private readonly Tokenizer _tokenizer = new();
    private readonly Parser _parser = new();
    private readonly TypeChecker _typeChecker = new();
    private string? _rootPath;
    private readonly Dictionary<string, Unit> _units = [];
    private readonly Dictionary<string, List<TopLevelNode>> _possiblyOutdatedTopLevelNodes = [];
    private ModuleRepository _repository = new([]);

    public void Init(string rootPath)
    {
        _rootPath = rootPath;
        Update();
    }

    public void Update()
    {
        if (_rootPath == null) return;
        var files = Directory.GetFiles(_rootPath, "*.nub", SearchOption.AllDirectories);
        foreach (var file in files)
        {
            var lastUpdated = File.GetLastWriteTimeUtc(file);
            var unit = _units.GetValueOrDefault(file);
            if (unit == null || lastUpdated > unit.FileTimestamp)
            {
                _units[file] = Update(file, lastUpdated);
            }
        }

        _repository = ModuleRepository.Create(_units.Select(x => x.Value.SyntaxTree).ToList());

        foreach (var (file, unit) in _units)
        {
            var topLevelNodes = _typeChecker.Check(unit.SyntaxTree, _repository);
            _possiblyOutdatedTopLevelNodes[file] = topLevelNodes;
            diagnosticsPublisher.Publish(file, [..unit.Diagnostics, .._typeChecker.Diagnostics]);
        }
    }

    private Unit Update(string file, DateTimeOffset lastUpdated)
    {
        var text = File.ReadAllText(file);
        var tokens = _tokenizer.Tokenize(file, text);
        var syntaxTree = _parser.Parse(tokens);

        return new Unit(syntaxTree, lastUpdated, [.._tokenizer.Diagnostics, .._parser.Diagnostics]);
    }

    public List<TopLevelNode> GetTopLevelNodes(string path)
    {
        return _possiblyOutdatedTopLevelNodes.GetValueOrDefault(path, []);
    }

    public ModuleRepository GetModuleRepository()
    {
        return _repository;
    }
}