using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
using EDPlayerJournal.Entries;

namespace EDPlayerJournal; 
public class JournalFile : IComparable<JournalFile>
{
    public string FullPath { get; set; }
    public int Part { get; set; }
    public DateTime? DateTime { get; set; }
    public DateTime? NormalisedDateTime { get; set; }

    public List<Entry> entries = new List<Entry>();

    private static Regex fileregex = new Regex("Journal\\.(\\d+)\\.(\\d+)\\.log");
    private static Regex update11regex = new Regex("Journal\\.([^\\.]+)\\.(\\d+).log");
    private static string iso8601 = "yyyyMMddTHHmmss";

    /// <summary>
    /// A public list of errors encountered while parsing the journal files
    /// </summary>
    public List<Exception> Errors { get; private set; } = new List<Exception>();

    public static bool VerifyFile(string path) {
        string filename = Path.GetFileName(path);

        var matches = fileregex.Matches(filename);
        if (matches.Count != 0) {
            return true;
        }

        matches = update11regex.Matches(filename);
        if (matches.Count != 0) {
            return true;
        }

        return false;
    }

    private void SetFilename(string path) {
        string filename = Path.GetFileName(path);
        if (!File.Exists(path) || filename == null) {
            throw new JournalException(string.Format("Invalid journal file: {0}", filename));
        }

        var matches = fileregex.Matches(filename);
        if (matches.Count == 0) {
            matches = update11regex.Matches(filename);
            if (matches.Count == 0) {
                throw new JournalException(string.Format("invalid file format: {0}", filename));
            }
        }

        FullPath = path;
        var groups = matches[0].Groups;
        string part = groups[2].ToString();
        string timestamp = groups[1].ToString();

        if (!timestamp.Contains("T")) {
            /* pre update 11 file
             */

            // The ISO is not quite correct on journal filenames, so build
            // a proper ISO 8601 date time stamp out of it.
            var date = new StringBuilder();
            date.Append("20"); // Add the missing century in front.
            date.Append(timestamp);
            date.Insert(8, "T"); // Add the "T" separating date and time

            timestamp = date.ToString();
        } else {
            /* post update 11 file, remove dashes
             */
            timestamp = timestamp.Replace("-", "");
        }

        this.DateTime = System.DateTime.ParseExact(timestamp, iso8601, CultureInfo.InvariantCulture);
        this.Part = int.Parse(part);
        this.NormalisedDateTime = System.DateTime.Parse(DateTime.Value.ToShortDateString());
    }

    public int CompareTo(JournalFile? other) {
        if (other == null || DateTime == null || other.DateTime == null) {
            return 0;
        }
        var comp = System.DateTime.Compare(DateTime.Value, other.DateTime.Value);
        if (comp == 0) {
            /* If we have the exact same datetime we compare by parts.
             */
            return Part - other.Part;
        } else {
            return comp;
        }
    }

    public JournalFile(string path) {
        FullPath = "";
        SetFilename(path);
    }

    public IEnumerable<Entry> Entries {
        get { 
            if (entries == null || entries.Count == 0) {
                try {
                    LoadEntries();
                } catch (IOException) {
                }
                if (entries == null) {
                    entries = new List<Entry>();
                }
            }
            return entries; 
        }
    }

    public void LoadEntries() {
        List<string> lines = new List<string>();

        /* This needs to be done this way, otherwise, if the game is still running the journal files cannot
         * be accessed. And it is very much convenient to generate logs while the game is still running.
         */
        using (var fs = new FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
            using (var sr = new StreamReader(fs, Encoding.UTF8)) {
                string? line;
                while ((line = sr.ReadLine()) != null) {
                    lines.Add(line);
                }
            }
        }

        entries.Clear();
        Errors.Clear();
        foreach (var line in lines) {
            // Skip empty lines
            if (line.Trim().Length == 0) {
                continue;
            }
            try {
                Entry? entry = Entry.Parse(line);
                if (entry != null) {
                    entries.Add(entry);
                }
            } catch (Exception ex) {
                Errors.Add(ex);
            }
        }
    }
}
