using Observatory.Framework.Files.Journal.Combat;
using Observatory.Framework.Files.Journal.Exploration;
using Observatory.Framework.Files.Journal.Odyssey;
using Observatory.Framework.Files.Journal.Other;
using Observatory.Framework.Files.Journal.Powerplay;
using Observatory.Framework.Files.Journal.Startup;
using Observatory.Framework.Files.Journal.StationServices;
using Observatory.Framework.Files.Journal.Trade;
using Observatory.Framework.Files.Journal.Travel;
namespace Pulsar.Utils;
using Observatory.Framework.Files.Journal;
[Flags]
public enum JournalReaderState
{
    /// 
    /// Have read the first character of the object
    /// 
    Start,
    /// 
    /// Have read the timestamp. Generally the first property in a journal entry.
    /// 
    Timestamp,
    /// 
    /// have read the event name. Generally the second property in a journal entry.
    /// 
    Event,
    /// 
    /// Have read the last character of the object, the next character should be a newline, whitespace, EOF, or another object.
    /// 
    End,
}
/// 
/// A JournalFile contains a collection of journal entries.
/// Each journal entry is a JSON object, delimited by a newline character.
/// all Journals can be deserialized into a JournalBase object for identification
/// and then deserialized into their respective types.
/// 
public class JournalJsonConverter(ILogger logger) : JsonConverter
{
    private JournalReaderState state = JournalReaderState.Start;
    public override JournalBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Utf8JsonReader clone = reader;
        DateTimeOffset? timestamp = null;
        string? eventName = null;
        // for debug
        int depth = 0;
        do
        {
            depth++;
            switch (clone.TokenType)
            {
                case JsonTokenType.StartObject:
                    state = JournalReaderState.Start;
                    break;
                case JsonTokenType.EndObject:
                    state = JournalReaderState.End;
                    break;
                case JsonTokenType.PropertyName:
                    var propertyName = clone.GetString();
                    // if we have not started reading the body, and we have not read the (timestamp or event name) 
                    if ((state & JournalReaderState.Timestamp) == 0 || (state & JournalReaderState.Event) == 0)
                    {
                        switch (propertyName)
                        {
                            case "timestamp":
                                clone.Read();
                                timestamp = clone.GetDateTimeOffset();
                                state = JournalReaderState.Timestamp;
                                break;
                            case "event":
                                clone.Read();
                                eventName = clone.GetString();
                                state = JournalReaderState.Event;
                                break;
                        }
                    }
                    if ((state & JournalReaderState.Event) != 0)
                    {
                        // create destination type
                        return GetDestinationType(ref reader, eventName!);
                    }
                    break;
                case JsonTokenType.Comment:
                    continue;
                case JsonTokenType.None:
                case JsonTokenType.StartArray:
                case JsonTokenType.EndArray:
                case JsonTokenType.String:
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                case JsonTokenType.Null:
                    logger.LogWarning("Unexpected token type {TokenType} at depth {Depth}", clone.TokenType, depth);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        } while (clone.Read());
        logger.LogWarning("Failed to deserialize journal entry at depth: {Depth}. Event?: {EventName}, Timestamp?: {Timestamp}", depth, eventName, timestamp);
        return null;
        // TODO: handle inf (invalid data) in the journal files
        // else if (typeof(TJournal) == typeof(Scan) && json.Contains("\"RotationPeriod\":inf"))
        // {
        //     deserialized = JsonSerializer.Deserialize(json.Replace("\"RotationPeriod\":inf,", ""));
        // }
    }
    private JournalBase GetDestinationType(ref Utf8JsonReader reader, string eventName)
    {
        switch (eventName)
        {
            case "Fileheader":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Commander":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Materials":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Rank":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Music":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Cargo":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Loadout":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Missions":
                return JsonSerializer.Deserialize(ref reader)!;
            case "FSSSignalDiscovered":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Reputation":
                return JsonSerializer.Deserialize(ref reader)!;
            case "LoadGame":
                return JsonSerializer.Deserialize(ref reader)!;
            case "ReceiveText":
                return JsonSerializer.Deserialize(ref reader)!;
            case "ShipLocker":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Location":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Powerplay":
                return JsonSerializer.Deserialize(ref reader)!;
            case "ReservoirReplenished":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Statistics":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Scan":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Shipyard":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Docked":
                return JsonSerializer.Deserialize(ref reader)!;
            case "LeaveBody":
                return JsonSerializer.Deserialize(ref reader)!;
            case "Progress":
                return JsonSerializer.Deserialize