using System.Text.Json.Serialization;

namespace AOW4.Data;

public sealed class ResourceAmounts
{
    public int Draft { get; set; }
    public int Food { get; set; }
    public int Knowledge { get; set; }
    public int Industry { get; set; }
    public int Magic { get; set; }
    public int Gold { get; set; }
    public int Imperial { get; set; }
    public int Stability { get; set; }

    [JsonIgnore]
    public bool IsZero => Draft == 0 && Food == 0 && Knowledge == 0 && Industry == 0 && Magic == 0 && Gold == 0 && Imperial == 0 && Stability == 0;

    public void Add(ResourceAmounts other)
    {
        Draft += other.Draft;
        Food += other.Food;
        Knowledge += other.Knowledge;
        Industry += other.Industry;
        Magic += other.Magic;
        Gold += other.Gold;
        Imperial += other.Imperial;
        Stability += other.Stability;
    }

    public void Subtract(ResourceAmounts other)
    {
        Draft -= other.Draft;
        Food -= other.Food;
        Knowledge -= other.Knowledge;
        Industry -= other.Industry;
        Magic -= other.Magic;
        Gold -= other.Gold;
        Imperial -= other.Imperial;
        Stability -= other.Stability;
    }

    public static ResourceAmounts operator +(ResourceAmounts a, ResourceAmounts b)
        => new ResourceAmounts
        {
            Draft = a.Draft + b.Draft,
            Food = a.Food + b.Food,
            Knowledge = a.Knowledge + b.Knowledge,
            Industry = a.Industry + b.Industry,
            Magic = a.Magic + b.Magic,
            Gold = a.Gold + b.Gold,
            Imperial = a.Imperial + b.Imperial,
            Stability = a.Stability + b.Stability
        };

    public static ResourceAmounts operator -(ResourceAmounts a, ResourceAmounts b)
        => new ResourceAmounts
        {
            Draft = a.Draft - b.Draft,
            Food = a.Food - b.Food,
            Knowledge = a.Knowledge - b.Knowledge,
            Industry = a.Industry - b.Industry,
            Magic = a.Magic - b.Magic,
            Gold = a.Gold - b.Gold,
            Imperial = a.Imperial - b.Imperial,
            Stability = a.Stability - b.Stability
        };
}

public sealed class BuildingDefinition
{
    public string Id { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public string? Description { get; set; }
    public string? Image { get; set; }
    public string SourceId { get; set; } = "General";
    public List<string> RequirementIds { get; set; } = new();
    public ResourceAmounts Income { get; set; } = new();
    public ResourceAmounts Upkeep { get; set; } = new();
    public ResourceAmounts Cost { get; set; } = new();
    public int BoostPopulation { get; set; }
    public int BoostForester { get; set; }
    public int BoostFarm { get; set; }
    public int BoostQuarry { get; set; }
    public int BoostGoldMine { get; set; }
    public int BoostConduit { get; set; }
}

public sealed class BuildOrderEntry
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public string ItemId { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public int RequestedTurn { get; set; }
    public int BuildStartTurn { get; set; }
    public int BuiltFinishTurn { get; set; }
    public int IndustryCostRemaining { get; set; }
    public BuildingDefinition Definition { get; set; } = new();
}

public sealed class ResourceSnapshot
{
    public int Turn { get; set; }
    public ResourceAmounts Stored { get; set; } = new();
    public ResourceAmounts TotalIncome { get; set; } = new();
    public ResourceAmounts TotalUpkeep { get; set; } = new();
    public string? Notes { get; set; }
}

public sealed class BuildPlanResult
{
    public List<BuildOrderEntry> BuildOrder { get; set; } = new();
    public List<ResourceSnapshot> ResourceHistory { get; set; } = new();
    public ResourceAmounts StartingPool { get; set; } = new();
}

public sealed class BuildOrderRequest
{
    public string ItemId { get; init; } = string.Empty;
    public BuildingDefinition Definition { get; init; } = new();
    public int RequestedTurn { get; init; }
}

public static class BuildingPlanCalculator
{
    public static BuildPlanResult CalculateBuildPlan(
        int totalTurns,
        IEnumerable<BuildOrderRequest> buildRequests,
        IEnumerable<BuildingDefinition>? startingBuildings = null,
        ResourceAmounts? startingResources = null)
    {
        var activeBuildings = new List<BuildOrderEntry>();
        var result = new BuildPlanResult
        {
            StartingPool = startingResources ?? new ResourceAmounts()
        };

        var stored = new ResourceAmounts();
        if (startingResources is not null)
        {
            stored = new ResourceAmounts
            {
                Draft = startingResources.Draft,
                Food = startingResources.Food,
                Knowledge = startingResources.Knowledge,
                Industry = startingResources.Industry,
                Magic = startingResources.Magic,
                Gold = startingResources.Gold,
                Imperial = startingResources.Imperial,
                Stability = startingResources.Stability
            };
        }

        if (startingBuildings is not null)
        {
            foreach (var startingBuilding in startingBuildings)
            {
                var entry = new BuildOrderEntry
                {
                    ItemId = startingBuilding.Id,
                    Name = startingBuilding.Name,
                    RequestedTurn = 1,
                    BuildStartTurn = 1,
                    BuiltFinishTurn = 0,
                    IndustryCostRemaining = 0,
                    Definition = startingBuilding
                };
                activeBuildings.Add(entry);
                result.BuildOrder.Add(entry);
            }
        }

        var requestsByTurn = buildRequests
            .OrderBy(r => r.RequestedTurn)
            .GroupBy(r => r.RequestedTurn)
            .ToDictionary(g => g.Key, g => g.ToList());

        var projects = new List<BuildOrderEntry>();

        for (var turn = 1; turn <= totalTurns; turn++)
        {
            var incomeThisTurn = new ResourceAmounts();
            var upkeepThisTurn = new ResourceAmounts();

            foreach (var building in activeBuildings)
            {
                if (building.BuiltFinishTurn == 0 || turn > building.BuiltFinishTurn)
                {
                    incomeThisTurn.Add(building.Definition.Income);
                    upkeepThisTurn.Add(building.Definition.Upkeep);
                }
            }

            stored.Add(incomeThisTurn);
            stored.Subtract(upkeepThisTurn);

            if (requestsByTurn.TryGetValue(turn, out var requests))
            {
                foreach (var request in requests)
                {
                    var nextProject = new BuildOrderEntry
                    {
                        ItemId = request.ItemId,
                        Name = request.Definition.Name,
                        RequestedTurn = turn,
                        BuildStartTurn = turn,
                        BuiltFinishTurn = 0,
                        IndustryCostRemaining = request.Definition.Cost.Industry,
                        Definition = request.Definition
                    };

                    stored.Subtract(new ResourceAmounts
                    {
                        Draft = request.Definition.Cost.Draft,
                        Food = request.Definition.Cost.Food,
                        Knowledge = request.Definition.Cost.Knowledge,
                        Magic = request.Definition.Cost.Magic,
                        Gold = request.Definition.Cost.Gold,
                        Imperial = request.Definition.Cost.Imperial,
                        Stability = 0
                    });

                    projects.Add(nextProject);
                    result.BuildOrder.Add(nextProject);
                }
            }

            var availableIndustry = incomeThisTurn.Industry;
            foreach (var project in projects.Where(p => p.BuiltFinishTurn == 0).OrderBy(p => p.RequestedTurn))
            {
                if (availableIndustry <= 0 || project.IndustryCostRemaining <= 0)
                {
                    continue;
                }

                var applied = Math.Min(availableIndustry, project.IndustryCostRemaining);
                project.IndustryCostRemaining -= applied;
                availableIndustry -= applied;

                if (project.IndustryCostRemaining <= 0)
                {
                    project.BuiltFinishTurn = turn;
                    activeBuildings.Add(project);
                }
            }

            result.ResourceHistory.Add(new ResourceSnapshot
            {
                Turn = turn,
                Stored = new ResourceAmounts
                {
                    Draft = stored.Draft,
                    Food = stored.Food,
                    Knowledge = stored.Knowledge,
                    Industry = stored.Industry,
                    Magic = stored.Magic,
                    Gold = stored.Gold,
                    Imperial = stored.Imperial,
                    Stability = stored.Stability
                },
                TotalIncome = incomeThisTurn,
                TotalUpkeep = upkeepThisTurn,
                Notes = null
            });
        }

        return result;
    }

    public static IReadOnlyList<BuildingDefinition> GetDefaultStartingBuildings()
    {
        return new List<BuildingDefinition>
        {
            new BuildingDefinition
            {
                Id = "town-hall-1",
                Name = "Town Hall I",
                Description = "Starting civic center that produces basic resources and morale.",
                SourceId = "General",
                Income = new ResourceAmounts
                {
                    Draft = 20,
                    Food = 30,
                    Industry = 20,
                    Gold = 0,
                    Stability = 10
                },
                Upkeep = new ResourceAmounts(),
                Cost = new ResourceAmounts()
            },
            new BuildingDefinition
            {
                Id = "throne",
                Name = "Throne",
                Description = "Royal treasury building that provides steady gold income.",
                SourceId = "General",
                Income = new ResourceAmounts
                {
                    Gold = 120
                },
                Upkeep = new ResourceAmounts(),
                Cost = new ResourceAmounts()
            }
        };
    }

    public static BuildPlanResult CreateSampleBuildPlan(int totalTurns = 60)
    {
        var sampleRequests = new List<BuildOrderRequest>
        {
            new BuildOrderRequest
            {
                ItemId = "farm-1",
                Definition = new BuildingDefinition
                {
                    Id = "farm-1",
                    Name = "Farm",
                    Description = "Provides food income and supports future growth.",
                    SourceId = "General",
                    Income = new ResourceAmounts { Food = 15 },
                    Upkeep = new ResourceAmounts { Gold = 2 },
                    Cost = new ResourceAmounts { Gold = 80, Industry = 40 }
                },
                RequestedTurn = 2
            },
            new BuildOrderRequest
            {
                ItemId = "workshop-1",
                Definition = new BuildingDefinition
                {
                    Id = "workshop-1",
                    Name = "Workshop",
                    Description = "Improves production and generates industry.",
                    SourceId = "General",
                    Income = new ResourceAmounts { Industry = 20 },
                    Upkeep = new ResourceAmounts { Gold = 4 },
                    Cost = new ResourceAmounts { Gold = 110, Industry = 80 }
                },
                RequestedTurn = 5
            },
            new BuildOrderRequest
            {
                ItemId = "market-1",
                Definition = new BuildingDefinition
                {
                    Id = "market-1",
                    Name = "Market",
                    Description = "Provides ongoing gold income and supports trade.",
                    SourceId = "General",
                    Income = new ResourceAmounts { Gold = 40 },
                    Upkeep = new ResourceAmounts { Gold = 6 },
                    Cost = new ResourceAmounts { Gold = 180, Industry = 100 }
                },
                RequestedTurn = 12
            }
        };

        return CalculateBuildPlan(totalTurns, sampleRequests, GetDefaultStartingBuildings(), new ResourceAmounts { Gold = 0 });
    }
}
