using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using com.itac.mes.imsapi.domain;
using com.itac.mes.imsapi.transport;

namespace com.itac.mes.imsapi.client.dotnet
{
    public abstract class IMSApiDotNetBase : IMSApiDotNetConstants, IDisposable
    {
        // Legacy 10.0.0-3 compatibility: properties are process-global and setProperty is static.
        // Existing binaries reference IMSApiDotNetBase::setProperty(string,string) with no instance (HasThis=false).
        protected static readonly Dictionary<string,string> properties=new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);

        // Legacy ArtesSettings compatibility. The original 10.0.0-3 DLL loads
        // ihas.properties once and resolves properties in this order:
        // 1) values set through IMSApiDotNetBase.setProperty(...),
        // 2) values from ihas.properties,
        // 3) server settings (not applicable to the local transport options here),
        // 4) caller supplied/default fallback.
        private static readonly Dictionary<string,string> fileProperties=new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
        private static bool filePropertiesLoaded;
        private static string propertyFilePath;

        protected IhapClient client;
        protected IMSApiServiceStub service;
        protected bool initialized;

        public static void setProperty(string key,string value)
        {
            if(key==null) throw new ArgumentNullException(nameof(key));
            lock(properties)
            {
                if(value==null) properties.Remove(key);
                else properties[key]=value;
            }
        }
        protected string Prop(string key,string fallback=null)
        {
            EnsureFilePropertiesLoaded();
            lock(properties)
            {
                string v;
                if(properties.TryGetValue(key,out v)) return v;
                if(fileProperties.TryGetValue(key,out v)) return v;
                return fallback;
            }
        }

        private static void EnsureFilePropertiesLoaded()
        {
            lock(properties)
            {
                if(filePropertiesLoaded) return;
                filePropertiesLoaded=true;
                fileProperties.Clear();

                try
                {
                    propertyFilePath=ResolvePropertyFile();
                    if(string.IsNullOrEmpty(propertyFilePath) || !File.Exists(propertyFilePath)) return;

                    using(var reader=new StreamReader(new FileStream(propertyFilePath,FileMode.Open,FileAccess.Read,FileShare.Read)))
                    {
                        string line;
                        while((line=reader.ReadLine())!=null)
                        {
                            line=line.Trim();
                            if(line.Length==0 || line.StartsWith("#",StringComparison.Ordinal)) continue;

                            // The legacy implementation splits at '=' and uses the first
                            // key/value pair. Keep that behavior rather than introducing a
                            // different .properties parser.
                            string[] parts=line.Split('=');
                            if(parts.Length<2) continue;
                            string key=parts[0].Trim();
                            string value=parts[1].Trim();
                            if(key.Length==0) continue;
                            fileProperties[key]=value;
                        }
                    }
                }
                catch
                {
                    // Legacy ArtesSettings treats property-file loading as non-fatal.
                    // imsapiInit must still be able to continue with programmatic
                    // properties/defaults if the file cannot be read.
                    fileProperties.Clear();
                }
            }
        }

        // Diagnostic helpers intentionally expose only the value physically read
        // from ihas.properties. They do NOT apply setProperty overrides. This makes
        // imsapiGetLibraryVersion useful for verifying the deployed property file.
        protected static string GetIhasPropertyForDiagnostics(string key)
        {
            EnsureFilePropertiesLoaded();
            lock(properties)
            {
                string value;
                return fileProperties.TryGetValue(key,out value) ? value : null;
            }
        }

        protected static string GetIhasPropertyFilePathForDiagnostics()
        {
            EnsureFilePropertiesLoaded();
            lock(properties)
            {
                return propertyFilePath;
            }
        }

        private static string ResolvePropertyFile()
        {
            const string fileName="ihas.properties";

            // FIX6 deployment rule: ihas.properties belongs next to the actually
            // loaded IMSApiDotNet.dll. This deliberately does NOT depend on the
            // process working directory and does NOT redirect the file lookup via
            // itac.propdir. The property itself remains available for legacy
            // callers, but the physical property file location is the DLL folder.
            string assemblyLocation=typeof(IMSApiDotNetBase).Assembly.Location;
            if(!string.IsNullOrWhiteSpace(assemblyLocation))
            {
                string assemblyDirectory=Path.GetDirectoryName(Path.GetFullPath(assemblyLocation));
                if(!string.IsNullOrWhiteSpace(assemblyDirectory))
                    return Path.Combine(assemblyDirectory,fileName);
            }

            // Assembly.Location can be empty for special hosting/load scenarios.
            // AppContext.BaseDirectory is the closest available runtime fallback
            // and still follows the deployed application/library location rather
            // than Directory.GetCurrentDirectory().
            string baseDirectory=AppContext.BaseDirectory;
            if(!string.IsNullOrWhiteSpace(baseDirectory))
                return Path.Combine(Path.GetFullPath(baseDirectory),fileName);

            // No working-directory fallback: by contract the file belongs to the
            // deployment directory of IMSApiDotNet.dll. If that directory cannot
            // be resolved, behave as if no property file exists.
            return null;
        }
        public virtual int imsapiInit()
        {
            if(initialized)return IMSAPI_INITIALIZED_ALREADY;
            try
            {
                var o=new IhapClientOptions();
                o.AppId=Prop("itac.appid","IMSApiDotNet"); o.AppType=Prop("itac.apptype",null);
                string direct=Prop("itac.artes.ihap.serviceurl",null); if(!string.IsNullOrWhiteSpace(direct))o.ServiceUrl=direct;
                string nodes=Prop("itac.artes.clusternodes",null);
                if(!string.IsNullOrWhiteSpace(nodes))foreach(var n in nodes.Split(new[]{',',';',' '},StringSplitOptions.RemoveEmptyEntries))o.ClusterNodes.Add(n.Trim());
                if(double.TryParse(Prop("itac.artes.connection.timeout",null),NumberStyles.Any,CultureInfo.InvariantCulture,out var ms))o.Timeout=TimeSpan.FromMilliseconds(ms);
                o.CompressRequests=IsTrue(Prop("itac.artes.ihap.compressed","false"));
                o.EnableFailover=!string.Equals(Prop("itac.artes.ihas.failover","true"),"false",StringComparison.OrdinalIgnoreCase);
                o.IgnoreNetworkPrefix=IsTrue(Prop("itac.artes.ihas.ignorenetworkprefix","false"));
                string ph=Prop("itac.artes.ihap.locator.httpProxyHost",null); string pp=Prop("itac.artes.ihap.locator.httpProxyPort",null);
                if(!string.IsNullOrWhiteSpace(ph)){int p=8080;int.TryParse(pp,out p);o.Proxy=new WebProxy(ph,p);}
                client=new IhapClient(o); service=new IMSApiServiceStub(client); initialized=true; return 0;
            }
            catch{return IMSAPI_LOCATOR_INIT_FAILED;}
        }
        public virtual int imsapiShutdown(){if(!initialized)return IMSAPI_NOT_INITIALIZED;Dispose();return 0;}
        public abstract int imsapiGetLibraryVersion(ref string libraryVersion);

        // Present on the legacy base class. Kept public here so binaries that called
        // these helpers directly as well as derived implementations keep resolving.
        public void logBegin(string methodName) { }
        public void logEnd(string methodName) { }
        protected static bool IsTrue(string v)=>string.Equals(v,"1")||string.Equals(v,"true",StringComparison.OrdinalIgnoreCase)||string.Equals(v,"yes",StringComparison.OrdinalIgnoreCase);
        protected int Guard(){return initialized?0:IMSAPI_NOT_INITIALIZED;}
        public virtual void Dispose(){service=null;if(client!=null){client.Dispose();client=null;}initialized=false;}
    }
}
