using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using MaxLib.Ini;

namespace Dacryptero
{
    public class Config
    {
        public class DBConfig
        {
            public string BaseDir { get; set; } = "db";
        }

        public class SchemaConfig
        {
            public string BaseDir { get; set; } = "schemas";
        }

        public class ServerConfig
        {
            public ushort Port { get; set; } = 8015;

            public IPAddress Interface { get; set; } = IPAddress.Loopback;

            public string Certificate { get; set; } = "";

            public string CertificateKey { get; set; } = "";

            public string UiDir { get; set; } = "ui";

            public string TemplateDir { get; set; } = "Templates";
        }

        public class SecurityConfig
        {
            public bool Tfa { get; set; } = true;
        }

        public class LogConfig
        {
            public Serilog.Events.LogEventLevel Console { get; set; }
                = Serilog.Events.LogEventLevel.Verbose;

            public string FileDir { get; set; } = "Logs";

            public Serilog.Events.LogEventLevel File { get; set; }
                = Serilog.Events.LogEventLevel.Verbose;
        }

        public DBConfig DB { get; } = new DBConfig();

        public SchemaConfig Schema { get; } = new SchemaConfig();

        public ServerConfig Server { get; } = new ServerConfig();

        public SecurityConfig Security { get; } = new SecurityConfig();

        public LogConfig Log { get; } = new LogConfig();

        public void Load(string file)
        {
            LoadFrom(file, new HashSet<string>());
            PostLoad();
        }

        private void LoadFrom(IniGroup group)
        {
            // Paths
            DB.BaseDir = group.GetString("db.baseDir", DB.BaseDir);
            Schema.BaseDir = group.GetString("schema.baseDir", Schema.BaseDir);
            // Server
            Server.Port = group.GetUInt16("server.port", Server.Port);
            try
            {
                var @interface = group.GetString("server.interface", null);
                if (@interface is not null && IPAddress.TryParse(@interface, out IPAddress? ip))
                    Server.Interface = ip;
            }
            catch {}
            Server.Certificate = group.GetString("server.certificate", Server.Certificate);
            Server.CertificateKey = group.GetString("server.certificateKey", Server.CertificateKey);
            Server.UiDir = group.GetString("server.uiDir", Server.UiDir);
            Server.TemplateDir = group.GetString("server.templateDir", Server.TemplateDir);
            // Security
            Security.Tfa = group.GetBool("security.2fa", Security.Tfa);
            // Logs
            Log.Console = group.GetEnum("log.console", Log.Console);
            Log.File = group.GetEnum("log.file", Log.File);
            Log.FileDir = group.GetString("log.file.dir", Log.FileDir);
        }

        private void LoadFrom(string file, HashSet<string> used)
        {
            file = new FileInfo(file).FullName;
            if (!used.Add(file))
                return;

            IniFile ini;
            try
            {
                ini = new MaxLib.Ini.Parser.IniParser().Parse(file);
            }
            catch { return; }
            LoadFrom(ini[0]);

            var dir = ini[0].GetString("config.include.dir", "");
            dir = ExtendPath(dir);
            if (Directory.Exists(dir))
                foreach (var entry in Directory.EnumerateFiles(dir, "*.ini"))
                {
                    LoadFrom(entry, used);
                }
        }

        private void PostLoad()
        {
            DB.BaseDir = ExtendPath(DB.BaseDir);
            Schema.BaseDir = ExtendPath(Schema.BaseDir);
            Server.Certificate = ExtendPath(Server.Certificate);
            Server.CertificateKey = ExtendPath(Server.CertificateKey);
            Server.UiDir = ExtendPath(Server.UiDir);
            Server.TemplateDir = ExtendPath(Server.TemplateDir);
            Log.FileDir = ExtendPath(Log.FileDir);
        }

        private string ExtendPath(string path)
        {
            path = Environment.ExpandEnvironmentVariables(path);
            if (path == "")
                return path;
            return Path.GetFullPath(path);
        }

        public static Config Default { get; } = new Config();
    }
}