using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using JsonRpc.Standard.Contracts;
using LanguageServer.VsCode.Contracts;
using Upsilon.BoundTypes;
using UpsilonLanguageServer.ReferenceHandler;

namespace UpsilonLanguageServer.Services
{
    [JsonRpcScope(MethodPrefix = "workspace/")]
    public class WorkspaceService : UpsilonLanguageServiceBase
    {
        public static string RootUri { get; set; }

        [JsonRpcMethod(IsNotification = true)]
        public async Task DidChangeConfiguration(SettingsRoot settings)
        {
            Session.Settings = settings.UpsilonLanguageServer;
            Session.Settings.ModuleDirectory =
                new DirectoryInfo(Path.Combine(RootUri, Session.Settings.ModuleDirectory)).FullName;
            foreach (var doc in Session.Documents.Values)
            {
                var diag = await DiagnosticProvider.LintDocument(doc.Document, doc, Session.Settings.MaxNumberOfProblems,
                    Session.Settings.ModuleDirectory);
                await Client.Document.PublishDiagnostics(doc.Document.Uri, diag);
            }
        }

        [JsonRpcMethod(IsNotification = true)]
        public async Task DidChangeWatchedFiles(ICollection<FileEvent> changes)
        {
            foreach (var change in changes)
            {
                if (!change.Uri.IsFile) continue;
                var localPath = change.Uri.AbsolutePath;
                if (string.Equals(Path.GetExtension(localPath), ".yup", StringComparison.InvariantCultureIgnoreCase))
                {
                    // If its a module
                    if (string.Equals(Path.GetDirectoryName(localPath), Session.Settings.ModuleDirectory))
                    {
                        var name = Path.GetFileNameWithoutExtension(localPath);
                        var firstScript = Session.Documents.Values.FirstOrDefault(x => x.Bound != null);
                        firstScript?.Bound.Script.Options.ModuleHandler.ClearCachedModule(name);

                        if (change.Type == FileChangeType.Deleted)
                        {
                            await Client.Document.PublishDiagnostics(change.Uri, new Diagnostic[0]);
                        }
                        foreach (var doc in Session.Documents.Values)
                        {
                            if (doc.Bound == null)
                                continue;
                            if (!doc.Bound.Script.ModuleDependencies.Any(x => string.Equals(x, name)))
                                continue;
                            var diag = await DiagnosticProvider.LintDocument(doc.Document, doc,
                                Session.Settings.MaxNumberOfProblems, Session.Settings.ModuleDirectory);
                            await Client.Document.PublishDiagnostics(doc.Document.Uri, diag);
                        }
                    }
                    // If the file has been removed, we will clear the lint result about it.
                    // Note that pass null to PublishDiagnostics may mess up the client.
                    if (change.Type == FileChangeType.Deleted)
                    {
                        await Client.Document.PublishDiagnostics(change.Uri, new Diagnostic[0]);
                    }
                }
                else if (string.Equals(Path.GetExtension(localPath), ".lua", StringComparison.InvariantCultureIgnoreCase))
                {
                    // If the file has been removed, we will clear the lint result about it.
                    // Note that pass null to PublishDiagnostics may mess up the client.
                    if (change.Type == FileChangeType.Deleted)
                    {
                        await Client.Document.PublishDiagnostics(change.Uri, new Diagnostic[0]);
                    }
                }
                else if (string.Equals(Path.GetExtension(localPath), ".json",
                    StringComparison.InvariantCultureIgnoreCase))
                {
                    var name = Path.GetFileName(localPath);
                    if (string.Equals(name, "types.json",
                        StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (change.Type == FileChangeType.Created || change.Type == FileChangeType.Changed)
                        {
                            BoundTypeParser.LoadBoundTypes(File.ReadAllText(localPath));
                            foreach (var doc in Session.Documents.Values)
                            {
                                var diag = await DiagnosticProvider.LintDocument(doc.Document, doc,
                                    Session.Settings.MaxNumberOfProblems, Session.Settings.ModuleDirectory);
                                await Client.Document.PublishDiagnostics(doc.Document.Uri, diag);
                            }
                        }
                    }
                    else if (string.Equals(name, "static.json",
                        StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (change.Type == FileChangeType.Created || change.Type == FileChangeType.Changed)
                        {
                            BoundTypeParser.LoadStaticVariables(File.ReadAllText(localPath));
                            foreach (var doc in Session.Documents.Values)
                            {
                                var diag = await DiagnosticProvider.LintDocument(doc.Document, doc,
                                    Session.Settings.MaxNumberOfProblems, Session.Settings.ModuleDirectory);
                                await Client.Document.PublishDiagnostics(doc.Document.Uri, diag);
                            }
                        }
                    }
                    else if (string.Equals(name, "references.json"))
                    {
                        try
                        {
                            LoadReferenceLibraries.ReloadReferences(RootUri, File.ReadAllText(localPath));
                            foreach (var doc in Session.Documents.Values)
                            {
                                var diag = await DiagnosticProvider.LintDocument(doc.Document, doc,
                                    Session.Settings.MaxNumberOfProblems, Session.Settings.ModuleDirectory);
                                await Client.Document.PublishDiagnostics(doc.Document.Uri, diag);
                            }
                        }
                        catch (Exception e)
                        {
                            await Client.Window.ShowMessage(MessageType.Error, e.ToString());
                        }
                    }
                }
            }
        }
    }
}
