// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License

namespace Azure.Iot.Operations.SchemaGenerator
{
    using System.Collections.Generic;
    using System.Linq;
    using Azure.Iot.Operations.CodeGeneration;
    using Azure.Iot.Operations.TDParser;
    using Azure.Iot.Operations.TDParser.Model;

    internal static class EventSchemaGenerator
    {
        internal static void GenerateEventSchemas(IResolvingThing resolvingThing, string projectName, Dictionary<string, List<SchemaSpec>> schemaSpecs, Dictionary<string, HashSet<SerializationFormat>> referencedSchemas)
        {
            FormInfo? subAllEventsForm = FormInfo.CreateFromForm(resolvingThing.ParsedThing.ErrorReporter, resolvingThing.ParsedThing.Thing.Forms?.Elements?.FirstOrDefault(f => f.Value.Op?.Elements?.Any(e => e.Value.Value == TDValues.OpSubAllEvents) ?? false)?.Value, resolvingThing.ParsedThing.Thing.SchemaDefinitions?.Entries);

            Dictionary<string, FieldSpec> valueFields = new();

            if (resolvingThing.ParsedThing.Thing.Events?.Entries != null)
            {
                foreach (KeyValuePair<string, ValueTracker<TDEvent>> eventKvp in resolvingThing.ParsedThing.Thing.Events.Entries.Where(e => e.Value.Value.Data != null))
                {
                    ProcessEvent(
                        resolvingThing.ParsedThing.ErrorReporter,
                        resolvingThing.ParsedThing.SchemaNamer,
                        eventKvp.Key,
                        eventKvp.Value.Value!,
                        projectName,
                        resolvingThing.ParsedThing.DirectoryName,
                        resolvingThing.ParsedThing.Thing.SchemaDefinitions?.Entries,
                        schemaSpecs,
                        valueFields);
                }
            }

            if (subAllEventsForm?.IncludeInherited ?? false)
            {
                foreach (InheritanceEnumerator inheritanceEnumerator in new InheritanceEnumeration(resolvingThing))
                {
                    foreach (KeyValuePair<string, ValueTracker<TDEvent>> eventKvp in inheritanceEnumerator.EnumerateEvents().Where(e => e.Value.Value.Data != null))
                    {
                        ProcessEvent(
                            inheritanceEnumerator.ParsedThing.ErrorReporter,
                            inheritanceEnumerator.ParsedThing.SchemaNamer,
                            eventKvp.Key,
                            eventKvp.Value.Value!,
                            projectName,
                            inheritanceEnumerator.ParsedThing.DirectoryName,
                            inheritanceEnumerator.ParsedThing.Thing.SchemaDefinitions?.Entries,
                            null,
                            valueFields);
                    }
                }
            }

            GenerateCollectiveEventObject(
                resolvingThing.ParsedThing.SchemaNamer,
                subAllEventsForm,
                valueFields,
                schemaSpecs);
        }

        private static void ProcessEvent(
            ErrorReporter errorReporter,
            SchemaNamer schemaNamer,
            string eventName,
            TDEvent tdEvent,
            string projectName,
            string dirName,
            Dictionary<string, ValueTracker<TDDataSchema>>? schemaDefinitions,
            Dictionary<string, List<SchemaSpec>>? schemaSpecs,
            Dictionary<string, FieldSpec> valueFields)
        {
            FormInfo? subEventForm = FormInfo.CreateFromForm(errorReporter, tdEvent.Forms?.Elements?.FirstOrDefault(f => f.Value.Op?.Elements?.Any(e => e.Value.Value == TDValues.OpSubEvent) ?? false)?.Value, schemaDefinitions);
            subEventForm ??= FormInfo.CreateFromForm(errorReporter, tdEvent.Forms?.Elements?.FirstOrDefault(f => f.Value.Op == null)?.Value, schemaDefinitions);

            FieldSpec dataFieldSpec = new(
                tdEvent.Description?.Value.Value ?? $"The '{eventName}' Event data value.",
                tdEvent.Data!,
                BackupSchemaName: schemaNamer.GetEventValueSchema(eventName),
                Require: true,
                Base: dirName,
                Fragment: tdEvent.Placeholder?.Value.Value ?? false,
                Inherited: schemaSpecs == null);
            valueFields[eventName] = dataFieldSpec with { Require = false };

            if (subEventForm?.TopicPattern != null && schemaSpecs != null)
            {
                string eventSchemaName = schemaNamer.GetEventSchema(eventName);
                ObjectSpec eventObjectSpec = new(
                    tdEvent.Description?.Value.Value ?? $"Container for the '{eventName}' Event data.",
                    new Dictionary<string, FieldSpec> { { eventName, dataFieldSpec } },
                    subEventForm.Format,
                    eventSchemaName,
                    TokenIndex: -1);
                if (!schemaSpecs.TryGetValue(eventSchemaName, out List<SchemaSpec>? eventSpecs))
                {
                    eventSpecs = new List<SchemaSpec>();
                    schemaSpecs[eventSchemaName] = eventSpecs;
                }
                eventSpecs.Add(eventObjectSpec);
            }
        }

        private static void GenerateCollectiveEventObject(
            SchemaNamer schemaNamer,
            FormInfo? topLevelEventsForm,
            Dictionary<string, FieldSpec> valueFields,
            Dictionary<string, List<SchemaSpec>> schemaSpecs)
        {
            if (topLevelEventsForm?.TopicPattern != null)
            {
                if (valueFields.Any())
                {
                    if (!schemaSpecs.TryGetValue(schemaNamer.AggregateEventSchema, out List<SchemaSpec>? aggEventSpecs))
                    {
                        aggEventSpecs = new List<SchemaSpec>();
                        schemaSpecs[schemaNamer.AggregateEventSchema] = aggEventSpecs;
                    }
                    aggEventSpecs.Add(new ObjectSpec(
                        $"Data values of Events.",
                        valueFields,
                        topLevelEventsForm.Format,
                        schemaNamer.AggregateEventSchema,
                        TokenIndex: -1));
                }
            }
        }
    }
}
