// Copyright (c) 2013-2016 Carsten Sonne Larsen <cs@innolan.dk>
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Data;
using Ntp.Common.Log;

namespace Ntp.Data.Log
{
    public static class LogExtensions
    {
        public static void SqlExecute(this LogBase log, string sql)
        {
            log.WriteLine(sql.Trim(), Severity.Trace);
        }

        public static void SqlExecute(this LogBase log, string sql, IDataParameterCollection parameters)
        {
            string query = $"{sql} ";
            foreach (IDataParameter parameter in parameters)
            {
                query = query.Replace($"{parameter.ParameterName} ", $"{GetDbDataParameterValue(parameter)} ");
                query = query.Replace($"{parameter.ParameterName},", $"{GetDbDataParameterValue(parameter)},");
                query = query.Replace($"{parameter.ParameterName};", $"{GetDbDataParameterValue(parameter)};");
                query = query.Replace($"{parameter.ParameterName})", $"{GetDbDataParameterValue(parameter)})");
            }

            log.WriteLine(query.Trim(), Severity.Trace);
        }

        internal static void ApplySchemaChanges(this LogBase log)
        {
            log.WriteLine(
                "Applying database changes...",
                Severity.Notice);
        }

        internal static void AwaitingDbLink(this LogBase log, IDbConnection connection)
        {
            log.WriteLine(
                "Waiting for database link.",
                Severity.Info);

            log.WriteLine(
                $"Database connection timeout is {connection.ConnectionTimeout} seconds.",
                Severity.Debug);

            log.WriteLine(
                "DATABASE CONNECTION STRING COULD CONTAIN SENSITIVE INFORMATION.",
                Severity.Trace);

            log.WriteLine(
                $"DATABASE CONNECTION STRING IS [{connection.ConnectionString}]",
                Severity.Trace);
        }

        internal static void DbLinkDown(this LogBase log)
        {
            log.WriteLine("Database link is down.", Severity.Notice);
        }

        internal static void DbLinkError(this LogBase log, Exception e)
        {
            log.WriteLine(e.Message, Severity.Info);
        }

        internal static void DbLinkUp(this LogBase log)
        {
            log.WriteLine("Database link is up.", Severity.Notice);
        }

        internal static void SchemaChangesApplied(this LogBase log, string version)
        {
            log.WriteLine($"Changes for version {version} applied.", Severity.Notice);
        }

        internal static void SchemaUpdated(this LogBase log, string version)
        {
            log.WriteLine($"Database schema updated to version {version}", Severity.Notice);
        }

        internal static void SchemaUpdateError(this LogBase log, Exception e)
        {
            log.WriteLine("Failed to apply database change script.", Severity.Error);
            log.WriteLine(e);
        }

        internal static void SchemaUpToDate(this LogBase log)
        {
            log.WriteLine("Database schema is latest version.", Severity.Info);
        }

        internal static void SchemaVersionError(this LogBase log, int database, int application)
        {
            if (database == -1)
            {
                log.WriteLine("Database version is -1. Did you set 'Create Yes' in configuration ?", Severity.Notice);
            }

            log.WriteLine(
                $"Application version is {application} but database version is {database}.",
                Severity.Error);

            if (database > application)
            {
                log.WriteLine("Update NTP Analyzer to resolve this problem.", Severity.Info);
            }
            else if (database < application)
            {
                log.WriteLine("Upgrade your database to resolve this problem.", Severity.Info);
            }
        }

        internal static void VersionTableCreated(this LogBase log)
        {
            log.WriteLine("Created new version table.", Severity.Notice);
        }

        internal static void VersionTableCreateError(this LogBase log, Exception e)
        {
            log.WriteLine("Failed to create version table.", Severity.Error);
            log.WriteLine(e);
        }

        internal static void VersionTableFetchError(this LogBase log, Exception e)
        {
            log.WriteLine("Failed to fetch version number from database.", Severity.Error);
            log.WriteLine(e);
        }

        internal static void VersionTableInsertError(this LogBase log, Exception e)
        {
            log.WriteLine("Failed to insert into version table.", Severity.Error);
            log.WriteLine(e);
        }

        internal static void VersionTableParamError(this LogBase log)
        {
            log.WriteLine("Internal error: Version cannot be null.", Severity.Error);
        }

        internal static void VersionTableUpdateError(this LogBase log, Exception e)
        {
            log.WriteLine("Failed to update version table.", Severity.Error);
            log.WriteLine(e);
        }

        private static string GetDbDataParameterValue(IDataParameter p)
        {
            switch (p.DbType)
            {
                case DbType.AnsiString:
                case DbType.AnsiStringFixedLength:
                case DbType.Guid:
                case DbType.String:
                case DbType.StringFixedLength:
                case DbType.Xml:
                    return $"\'{p.Value.ToString().Replace("'", "''")}\'";
                case DbType.Time:
                    return p.Value.ToString();
                case DbType.Date:
                case DbType.DateTime:
                    return $"\'{Convert.ToDateTime(p.Value).ToString("yyyy-MM-dd HH:mm:ss")}\'";
                case DbType.DateTime2:
                case DbType.DateTimeOffset:
                    return p.Value.ToString();
                case DbType.Object:
                    return p.Value.ToString();
                case DbType.Binary:
                    return "{binary}";
                case DbType.Boolean:
                    return Convert.ToBoolean(p.Value) ? "1" : "0";
                case DbType.Byte:
                case DbType.SByte:
                case DbType.Int16:
                case DbType.Int32:
                case DbType.Int64:
                case DbType.UInt16:
                case DbType.UInt32:
                case DbType.UInt64:
                    return p.Value.ToString();
                case DbType.Single:
                case DbType.Double:
                case DbType.Decimal:
                case DbType.Currency:
                case DbType.VarNumeric:
                    return p.Value.ToString();
                default:
                    return p.Value.ToString();
            }
        }
    }
}