#nullable enable

using SQLite4Unity3d;
using System;
using System.IO;
using UnityEngine;


namespace UVC.Log
{
    /// <summary>
    /// ServerLog는 HTTP 요청과 MQTT 메시지를 기록하는 기능을 제공합니다.
    /// </summary>
    public class ServerLog
    {

        private static bool doDeletedHttp = false;
        private static bool doDeletedMqtt = false;

        private static SQLiteConnection? db;

        private static readonly object _dbLock = new object();

        static ServerLog()
        {
            try
            {
                CheckDatabaseConnection();
                if (doDeletedHttp == false)
                {
                    //한달 지난 로그 삭제
                    db.Execute("DELETE FROM HttpLogEntry WHERE RequestDate < datetime('now', '-1 month', 'localtime');");
                    doDeletedHttp = true;
                }
                if (doDeletedMqtt == false)
                {
                    //한달 지난 로그 삭제
                    db.Execute("DELETE FROM MqttLogEntry WHERE Date < datetime('now', '-1 month', 'localtime');");
                    doDeletedMqtt = true;
                }
            }
            catch (Exception ex)
            {
                Debug.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// HTTP 요청을 로깅하여 데이터베이스에 로그 항목을 생성하고 저장합니다.
        /// </summary>
        /// <remarks>이 메소드는 스레드 안전하며 로그 항목이 동기화된 방식으로 데이터베이스에 저장되도록 보장합니다.
        /// 호출자는 유효한 입력 값을 제공할 책임이 있습니다.</remarks>
        /// <param name="url">HTTP 요청의 URL입니다.</param>
        /// <param name="method">요청에 사용된 HTTP 메소드(예: GET, POST)입니다.</param>
        /// <param name="headerJson">JSON 형식의 HTTP 요청 헤더입니다.</param>
        /// <param name="bodyString">HTTP 요청의 본문을 문자열로 표현한 것입니다.</param>
        /// <param name="date">HTTP 요청의 날짜와 시간을 문자열로 표현한 것입니다.</param>
        /// <returns>로깅된 HTTP 요청을 나타내는 <see cref="HttpLogEntry"/> 객체입니다. 반환된 객체는
        /// 요청 URL, 메소드, 헤더, 본문, 날짜와 같은 세부 정보를 포함합니다.</returns>
        public static HttpLogEntry LogHttpRequest(string url, string method, string headerJson, string bodyString, string date)
        {
            var logEntry = new HttpLogEntry
            {
                RequestURL = url,
                RequestMethod = method,
                RequestHeader = headerJson,
                RequestBody = bodyString,
                RequestDate = date,
            };
            lock (_dbLock)
            {
                db.Insert(logEntry);
                return logEntry; // 반환값으로 ID를 반환
            }
        }

        /// <summary>
        /// HTTP 응답의 세부 정보를 데이터베이스에 기록합니다.
        /// </summary>
        /// <remarks>이 메소드는 스레드 안전하며 로깅 작업이 동기화되도록 보장합니다.</remarks>
        /// <param name="httpLogEntry">기록할 응답의 세부 정보가 포함된 HTTP 로그 항목입니다. 이 매개변수는 <see
        /// langword="null"/>일 수 없습니다.</param>
        public static void LogHttpResponse(HttpLogEntry httpLogEntry)
        {   
            lock (_dbLock)
            {
                db.Update(httpLogEntry);
            }
        }

        /// <summary>
        /// MQTT 메시지를 데이터베이스에 기록하고 생성된 로그 항목을 반환합니다.
        /// </summary>
        /// <remarks>이 메서드는 MQTT 메시지에 대한 새로운 로그 항목을 생성하여 데이터베이스에 저장합니다. 이 메서드는 스레드 안전하며 데이터베이스 작업이 동기화되도록 보장합니다.</remarks>
        /// <param name="url">MQTT 브로커의 URL입니다.</param>
        /// <param name="port">MQTT 브로커에 연결하는 데 사용된 포트입니다.</param>
        /// <param name="topic">MQTT 메시지의 토픽입니다.</param>
        /// <param name="payload">MQTT 메시지의 페이로드입니다.</param>
        /// <param name="date">메시지가 수신된 날짜와 시간(문자열 형식)입니다.</param>
        /// <param name="exception">처리 중 오류가 발생한 경우의 예외 메시지(옵션). <see langword="null"/>일 수 있습니다.</param>
        /// <returns>기록된 MQTT 메시지를 나타내는 <see cref="MqttLogEntry"/> 객체입니다.</returns>
        public static MqttLogEntry LogMqtt(string url, string port, string topic, string payload, string date, string? exception = null)
        {
            var logEntry = new MqttLogEntry
            {
                URL = url,
                Port = port,
                Topic = topic,
                Payload = payload,
                Date = date,
                Exception = exception
            };
            lock (_dbLock)
            {
                db?.Insert(logEntry);
                return logEntry;
            }
        }


        protected static void CheckDatabaseConnection()
        {
            if (db == null)
            {
                string dbPath = string.Empty;
                // unity runtime 일때 
                if (Application.platform == RuntimePlatform.WindowsPlayer)
                {
                    string unityLogsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"unityLogs");
                    if (!Directory.Exists(unityLogsPath)) Directory.CreateDirectory(unityLogsPath);
                    dbPath = Path.Combine(unityLogsPath, @"unityLogs\serverLog.db");
                }
                else
                {
                    // unity editor 일때 
                    DirectoryInfo di = new DirectoryInfo(Application.dataPath).Parent;
                    //di 하위에 Logs 디렉토리가 없으면 생성
                    DirectoryInfo di2 = new DirectoryInfo(Path.Combine(di.FullName, @"Logs"));
                    if (!di2.Exists) di.CreateSubdirectory("Logs");
                    string unityLogsPath = Path.Combine(di2.FullName, @"unityLogs");
                    if (!Directory.Exists(unityLogsPath)) Directory.CreateDirectory(unityLogsPath);
                    dbPath = Path.Combine(unityLogsPath, @"serverLog.db");
                }

                db = new SQLiteConnection(dbPath);
                db.CreateTable<HttpLogEntry>();
                db.CreateTable<MqttLogEntry>();
            }
        }
    }

    
    /// <summary>
    /// HTTP 요청 및 관련 응답을 위한 로그 항목을 나타냅니다.
    /// </summary>
    /// <remarks>
    /// 이 클래스는 HTTP 요청의 URL, 메소드, 헤더, 본문과 응답 데이터를 (가능한 경우) 포함한 세부 정보를 저장하는 데 사용됩니다.
    /// 또한 요청 또는 응답 처리 중 발생한 예외 사항도 기록합니다.
    /// </remarks>
    public class HttpLogEntry
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
        public string RequestURL { get; set; }
        public string RequestMethod { get; set; }
        public string RequestHeader { get; set; }
        public string RequestBody { get; set; }
        public string RequestDate { get; set; }
        public string? ResponseData { get; set; }
        public string? ResponseDate { get; set; }
        public string? Exception { get; set; }
    }

    
    /// <summary>
    /// MQTT 메시지에 대한 로그 항목을 나타냅니다. 연결, 토픽, 페이로드 및 모든 관련 예외에 대한 세부 정보를 포함합니다.
    /// </summary>
    /// <remarks>이 클래스는 일반적으로 로깅 또는 디버깅 목적으로 MQTT 메시지에 대한 정보를 저장하고 검색하는 데 사용됩니다.
    /// 각 로그 항목에는 URL, 포트, 토픽, 페이로드 및 메시지가 기록된 날짜와 같은 세부 정보가 포함됩니다.
    /// MQTT 작업 중 예외가 발생한 경우 <see cref="Exception"/> 속성에도 기록될 수 있습니다.</remarks>
    public class MqttLogEntry
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
        public string URL { get; set; }
        public string Port { get; set; }
        public string Topic { get; set; }
        public string Payload { get; set; }
        public string Date { get; set; }
        public string? Exception { get; set; }
    }
}
