diff --git a/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Services/AllianceService.cs b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Services/AllianceService.cs
new file mode 100644
index 0000000..394cc83
--- /dev/null
+++ b/ShadowedRealmsMobile/src/server/ShadowedRealms.API/Services/AllianceService.cs
@@ -0,0 +1,1874 @@
+/*
+ * File: D:\shadowed-realms-mobile\ShadowedRealmsMobile\src\server\ShadowedRealms.API\Services\AllianceService.cs
+ * Created: 2025-10-19
+ * Last Modified: 2025-10-19
+ * Description: Concrete implementation of IAllianceService providing comprehensive alliance-related business logic operations including coalition system (core innovation), research trees, territory management, democratic decision-making, and multi-alliance coordination while preserving individual alliance identity
+ * Last Edit Notes: Initial creation with complete business logic implementation
+ */
+
+using Microsoft.Extensions.Logging;
+using ShadowedRealms.Core.Interfaces;
+using ShadowedRealms.Core.Interfaces.Repositories;
+using ShadowedRealms.Core.Interfaces.Services;
+using ShadowedRealms.Core.Models;
+using ShadowedRealms.Core.Models.Player;
+
+namespace ShadowedRealms.API.Services
+{
+ ///
+ /// Concrete implementation of alliance service providing comprehensive business logic coordination for all alliance-related operations
+ ///
+ public class AllianceService : IAllianceService
+ {
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly IAllianceRepository _allianceRepository;
+ private readonly IPlayerRepository _playerRepository;
+ private readonly IKingdomRepository _kingdomRepository;
+ private readonly ICombatLogRepository _combatLogRepository;
+ private readonly IPurchaseLogRepository _purchaseLogRepository;
+ private readonly ILogger _logger;
+
+ // Alliance constants for balance and limits
+ private const int MAX_ALLIANCE_SIZE = 100; // Maximum members per alliance
+ private const int MIN_COALITION_MEMBERS = 50; // Minimum combined members for coalition
+ private const double COALITION_VOTING_QUORUM = 0.6; // 60% member participation required
+ private const int MAX_RESEARCH_LEVEL = 50; // Maximum research level per branch
+ private const int MAX_ALLIANCE_LEVEL = 30; // Maximum alliance level
+ private const double DEMOCRATIC_VOTING_QUORUM = 0.5; // 50% member participation for decisions
+
+ public AllianceService(
+ IUnitOfWork unitOfWork,
+ IAllianceRepository allianceRepository,
+ IPlayerRepository playerRepository,
+ IKingdomRepository kingdomRepository,
+ ICombatLogRepository combatLogRepository,
+ IPurchaseLogRepository purchaseLogRepository,
+ ILogger logger)
+ {
+ _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
+ _allianceRepository = allianceRepository ?? throw new ArgumentNullException(nameof(allianceRepository));
+ _playerRepository = playerRepository ?? throw new ArgumentNullException(nameof(playerRepository));
+ _kingdomRepository = kingdomRepository ?? throw new ArgumentNullException(nameof(kingdomRepository));
+ _combatLogRepository = combatLogRepository ?? throw new ArgumentNullException(nameof(combatLogRepository));
+ _purchaseLogRepository = purchaseLogRepository ?? throw new ArgumentNullException(nameof(purchaseLogRepository));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ #region Coalition System (Core Innovation)
+
+ public async Task<(bool Success, string CoalitionId, Dictionary VotingRequirements, DateTime VotingDeadline)>
+ FormCoalitionAsync(int initiatingAllianceId, List targetAllianceIds, int kingdomId, string coalitionType,
+ Dictionary proposedTerms)
+ {
+ _logger.LogInformation("Forming coalition: Alliance {InitiatingId} with targets {TargetIds}, Type: {CoalitionType}",
+ initiatingAllianceId, string.Join(",", targetAllianceIds), coalitionType);
+
+ return await _unitOfWork.ExecuteInTransactionAsync(async () =>
+ {
+ // Validate initiating alliance
+ var initiatingAlliance = await _allianceRepository.GetByIdAsync(initiatingAllianceId, kingdomId);
+ if (initiatingAlliance == null)
+ throw new ArgumentException($"Initiating alliance {initiatingAllianceId} not found");
+
+ // Validate target alliances
+ var targetAlliances = new List();
+ var totalMembers = initiatingAlliance.MemberCount;
+
+ foreach (var targetId in targetAllianceIds)
+ {
+ var alliance = await _allianceRepository.GetByIdAsync(targetId, kingdomId);
+ if (alliance == null)
+ {
+ return (false, "", new Dictionary
+ {
+ ["Error"] = $"Target alliance {targetId} not found"
+ }, DateTime.UtcNow);
+ }
+ targetAlliances.Add(alliance);
+ totalMembers += alliance.MemberCount;
+ }
+
+ // Validate coalition requirements
+ if (totalMembers < MIN_COALITION_MEMBERS)
+ {
+ return (false, "", new Dictionary
+ {
+ ["Error"] = $"Coalition requires minimum {MIN_COALITION_MEMBERS} combined members, has {totalMembers}"
+ }, DateTime.UtcNow);
+ }
+
+ // Generate unique coalition ID
+ var coalitionId = $"COALITION_{initiatingAllianceId}_{DateTime.UtcNow.Ticks}";
+
+ // Calculate voting requirements
+ var votingRequirements = new Dictionary
+ {
+ ["CoalitionId"] = coalitionId,
+ ["InitiatingAlliance"] = new { Id = initiatingAllianceId, Name = initiatingAlliance.Name },
+ ["TargetAlliances"] = targetAlliances.Select(a => new { Id = a.AllianceId, Name = a.Name }).ToList(),
+ ["CoalitionType"] = coalitionType,
+ ["ProposedTerms"] = proposedTerms,
+ ["RequiredQuorum"] = COALITION_VOTING_QUORUM,
+ ["TotalMembers"] = totalMembers,
+ ["VotingDeadline"] = DateTime.UtcNow.AddDays(3) // 3 days to vote
+ };
+
+ // Create coalition proposal
+ var coalitionProposal = new Dictionary
+ {
+ ["CoalitionId"] = coalitionId,
+ ["InitiatingAllianceId"] = initiatingAllianceId,
+ ["TargetAllianceIds"] = targetAllianceIds,
+ ["CoalitionType"] = coalitionType,
+ ["ProposedTerms"] = proposedTerms,
+ ["Status"] = "Voting",
+ ["CreatedAt"] = DateTime.UtcNow,
+ ["VotingDeadline"] = votingRequirements["VotingDeadline"]
+ };
+
+ // Store coalition proposal (would be in coalition management system)
+ await StoreCoalitionProposal(coalitionId, coalitionProposal, kingdomId);
+
+ // Notify all target alliances of coalition proposal
+ foreach (var alliance in targetAlliances)
+ {
+ await NotifyAllianceOfCoalitionProposal(alliance.AllianceId, kingdomId, coalitionProposal);
+ }
+
+ _logger.LogInformation("Coalition proposal created: {CoalitionId}, Voting deadline: {Deadline}",
+ coalitionId, votingRequirements["VotingDeadline"]);
+
+ return (true, coalitionId, votingRequirements, (DateTime)votingRequirements["VotingDeadline"]);
+ });
+ }
+
+ public async Task<(bool VotePassed, double ApprovalPercentage, Dictionary CoalitionStatus)>
+ ProcessCoalitionVotingAsync(int allianceId, string coalitionId, int kingdomId,
+ Dictionary votingResults)
+ {
+ _logger.LogInformation("Processing coalition voting: Alliance {AllianceId}, Coalition {CoalitionId}, Votes: {VoteCount}",
+ allianceId, coalitionId, votingResults.Count);
+
+ return await _unitOfWork.ExecuteInTransactionAsync(async () =>
+ {
+ var alliance = await _allianceRepository.GetByIdAsync(allianceId, kingdomId);
+ if (alliance == null)
+ throw new ArgumentException($"Alliance {allianceId} not found");
+
+ // Get coalition proposal
+ var coalitionProposal = await GetCoalitionProposal(coalitionId, kingdomId);
+ if (coalitionProposal == null)
+ {
+ return (false, 0.0, new Dictionary { ["Error"] = "Coalition proposal not found" });
+ }
+
+ // Validate voting eligibility
+ var eligibleVoters = await GetAllianceActiveMembers(allianceId, kingdomId);
+ var validVotes = 0;
+ var approvalVotes = 0;
+
+ foreach (var vote in votingResults)
+ {
+ var playerId = vote.Key;
+ var voteValue = vote.Value;
+
+ // Check if player is eligible to vote
+ if (eligibleVoters.Any(v => v.PlayerId == playerId))
+ {
+ validVotes++;
+ if (voteValue) approvalVotes++;
+ }
+ }
+
+ var totalEligibleVoters = eligibleVoters.Count;
+ var participationRate = (double)validVotes / totalEligibleVoters;
+ var approvalPercentage = validVotes > 0 ? (double)approvalVotes / validVotes : 0.0;
+
+ var coalitionStatus = new Dictionary
+ {
+ ["CoalitionId"] = coalitionId,
+ ["AllianceId"] = allianceId,
+ ["AllianceName"] = alliance.Name,
+ ["TotalEligibleVoters"] = totalEligibleVoters,
+ ["VotesReceived"] = validVotes,
+ ["ParticipationRate"] = participationRate,
+ ["ApprovalVotes"] = approvalVotes,
+ ["RejectionVotes"] = validVotes - approvalVotes,
+ ["ApprovalPercentage"] = approvalPercentage,
+ ["QuorumMet"] = participationRate >= COALITION_VOTING_QUORUM,
+ ["VotePassed"] = participationRate >= COALITION_VOTING_QUORUM && approvalPercentage >= 0.6
+ };
+
+ // Store voting results
+ await StoreCoalitionVotingResults(coalitionId, allianceId, kingdomId, coalitionStatus);
+
+ var votePassed = (bool)coalitionStatus["VotePassed"];
+
+ if (votePassed)
+ {
+ _logger.LogInformation("Coalition vote passed for Alliance {AllianceId}: {ApprovalPercentage}% approval",
+ allianceId, approvalPercentage * 100);
+ }
+ else
+ {
+ _logger.LogInformation("Coalition vote failed for Alliance {AllianceId}: {ApprovalPercentage}% approval, {ParticipationRate}% participation",
+ allianceId, approvalPercentage * 100, participationRate * 100);
+ }
+
+ return (votePassed, approvalPercentage, coalitionStatus);
+ });
+ }
+
+ public async Task> ManageCoalitionOperationAsync(string coalitionId, int kingdomId,
+ string operationType, Dictionary operationDetails)
+ {
+ _logger.LogInformation("Managing coalition operation: {CoalitionId}, Type: {OperationType}",
+ coalitionId, operationType);
+
+ return await _unitOfWork.ExecuteInTransactionAsync(async () =>
+ {
+ var coalition = await GetActiveCoalition(coalitionId, kingdomId);
+ if (coalition == null)
+ {
+ return new Dictionary { ["Error"] = "Active coalition not found" };
+ }
+
+ var operationResult = new Dictionary
+ {
+ ["CoalitionId"] = coalitionId,
+ ["OperationType"] = operationType,
+ ["OperationTimestamp"] = DateTime.UtcNow
+ };
+
+ switch (operationType.ToLower())
+ {
+ case "joint_research":
+ operationResult = await ProcessCoalitionJointResearch(coalition, operationDetails, kingdomId);
+ break;
+
+ case "territory_coordination":
+ operationResult = await ProcessCoalitionTerritoryCoordination(coalition, operationDetails, kingdomId);
+ break;
+
+ case "kvk_preparation":
+ operationResult = await ProcessCoalitionKvKPreparation(coalition, operationDetails, kingdomId);
+ break;
+
+ case "resource_sharing":
+ operationResult = await ProcessCoalitionResourceSharing(coalition, operationDetails, kingdomId);
+ break;
+
+ case "strategic_planning":
+ operationResult = await ProcessCoalitionStrategicPlanning(coalition, operationDetails, kingdomId);
+ break;
+
+ default:
+ operationResult["Error"] = $"Unknown operation type: {operationType}";
+ break;
+ }
+
+ // Update coalition activity log
+ await LogCoalitionActivity(coalitionId, kingdomId, operationType, operationResult);
+
+ _logger.LogInformation("Coalition operation completed: {CoalitionId}, Type: {OperationType}",
+ coalitionId, operationType);
+
+ return operationResult;
+ });
+ }
+
+ public async Task<(bool Success, Dictionary BenefitDistribution, List AffectedAlliances)>
+ DissolveCoalitionAsync(string coalitionId, int kingdomId, string dissolutionReason)
+ {
+ _logger.LogInformation("Dissolving coalition: {CoalitionId}, Reason: {Reason}",
+ coalitionId, dissolutionReason);
+
+ return await _unitOfWork.ExecuteInTransactionAsync(async () =>
+ {
+ var coalition = await GetActiveCoalition(coalitionId, kingdomId);
+ if (coalition == null)
+ {
+ return (false, new Dictionary { ["Error"] = "Coalition not found" }, new List());
+ }
+
+ var memberAllianceIds = (List)coalition["MemberAllianceIds"];
+ var benefitDistribution = new Dictionary();
+
+ // Calculate accumulated coalition benefits
+ var accumulatedBenefits = await CalculateAccumulatedCoalitionBenefits(coalitionId, kingdomId);
+
+ // Distribute benefits fairly among member alliances
+ foreach (var allianceId in memberAllianceIds)
+ {
+ var alliance = await _allianceRepository.GetByIdAsync(allianceId, kingdomId);
+ if (alliance != null)
+ {
+ var allianceBenefits = CalculateAllianceShareOfCoalitionBenefits(
+ alliance, accumulatedBenefits, memberAllianceIds.Count);
+
+ await ApplyDissolutionBenefits(allianceId, kingdomId, allianceBenefits);
+ benefitDistribution[$"Alliance_{allianceId}"] = allianceBenefits;
+ }
+ }
+
+ // Mark coalition as dissolved
+ await MarkCoalitionAsDissolved(coalitionId, kingdomId, dissolutionReason);
+
+ // Restore individual alliance autonomy
+ foreach (var allianceId in memberAllianceIds)
+ {
+ await RestoreAllianceAutonomy(allianceId, kingdomId);
+ }
+
+ // Notify all member alliances
+ foreach (var allianceId in memberAllianceIds)
+ {
+ await NotifyAllianceOfDissolution(allianceId, kingdomId, dissolutionReason, benefitDistribution);
+ }
+
+ _logger.LogInformation("Coalition dissolved: {CoalitionId}, Affected alliances: {Count}",
+ coalitionId, memberAllianceIds.Count);
+
+ return (true, benefitDistribution, memberAllianceIds);
+ });
+ }
+
+ #endregion
+
+ #region Research Trees & Collective Benefits
+
+ public async Task<(bool Success, int NewResearchLevel, Dictionary UnlockedBenefits,
+ Dictionary ContributionTracking)>
+ ProcessAllianceResearchAsync(int allianceId, int kingdomId, string researchType, string researchNode,
+ Dictionary> memberContributions)
+ {
+ _logger.LogInformation("Processing alliance research: Alliance {AllianceId}, Type: {ResearchType}, Node: {ResearchNode}",
+ allianceId, researchType, researchNode);
+
+ return await _unitOfWork.ExecuteInTransactionAsync(async () =>
+ {
+ var alliance = await _allianceRepository.GetByIdAsync(allianceId, kingdomId);
+ if (alliance == null)
+ throw new ArgumentException($"Alliance {allianceId} not found");
+
+ // Validate research prerequisites
+ var (canResearch, prerequisites, requiredContributions, estimatedBenefits) =
+ await ValidateResearchRequirementsAsync(allianceId, kingdomId, researchType, researchNode);
+
+ if (!canResearch)
+ {
+ return (false, 0, new Dictionary
+ {
+ ["Error"] = string.Join("; ", prerequisites)
+ }, new Dictionary());
+ }
+
+ // Validate member contributions
+ var contributionTracking = new Dictionary();
+ var totalContributions = new Dictionary();
+
+ foreach (var memberContribution in memberContributions)
+ {
+ var playerId = memberContribution.Key;
+ var resources = memberContribution.Value;
+
+ // Validate player is alliance member
+ var player = await _playerRepository.GetByIdAsync(playerId, kingdomId);
+ if (player?.AllianceId != allianceId)
+ {
+ return (false, 0, new Dictionary
+ {
+ ["Error"] = $"Player {playerId} is not a member of alliance {allianceId}"
+ }, new Dictionary());
+ }
+
+ // Calculate contribution value for tracking
+ var contributionValue = resources.Values.Sum();
+ contributionTracking[playerId] = contributionValue;
+
+ // Add to total contributions
+ foreach (var resource in resources)
+ {
+ totalContributions[resource.Key] = totalContributions.GetValueOrDefault(resource.Key, 0) + resource.Value;
+ }
+ }
+
+ // Check if contributions meet requirements
+ foreach (var requirement in requiredContributions)
+ {
+ var resourceType = requirement.Key;
+ var requiredAmount = (long)requirement.Value;
+ var contributedAmount = totalContributions.GetValueOrDefault(resourceType, 0);
+
+ if (contributedAmount < requiredAmount)
+ {
+ return (false, 0, new Dictionary
+ {
+ ["Error"] = $"Insufficient {resourceType}: Need {requiredAmount:N0}, Have {contributedAmount:N0}"
+ }, contributionTracking);
+ }
+ }
+
+ // Spend resources from contributing members
+ foreach (var memberContribution in memberContributions)
+ {
+ var playerId = memberContribution.Key;
+ var resources = memberContribution.Value;
+
+ var player = await _playerRepository.GetByIdAsync(playerId, kingdomId);
+ foreach (var resource in resources)
+ {
+ var currentAmount = GetPlayerResourceAmount(player, resource.Key);
+ if (currentAmount < resource.Value)
+ {
+ return (false, 0, new Dictionary
+ {
+ ["Error"] = $"Player {playerId} has insufficient {resource.Key}"
+ }, contributionTracking);
+ }
+ }
+
+ // Apply resource spending
+ await SpendPlayerResources(playerId, kingdomId, resources,
+ $"Alliance research: {researchType} - {researchNode}");
+ }
+
+ // Advance research level
+ var currentLevel = alliance.ResearchLevels.GetValueOrDefault(researchNode, 0);
+ var newLevel = Math.Min(currentLevel + 1, MAX_RESEARCH_LEVEL);
+
+ var updatedResearchLevels = new Dictionary(alliance.ResearchLevels)
+ {
+ [researchNode] = newLevel
+ };
+
+ await _allianceRepository.UpdateResearchLevelsAsync(allianceId, kingdomId, updatedResearchLevels);
+
+ // Calculate unlocked benefits
+ var unlockedBenefits = CalculateResearchBenefits(researchType, researchNode, newLevel);
+
+ // Apply benefits to all alliance members
+ await ApplyCollectiveResearchBenefitsAsync(allianceId, kingdomId);
+
+ // Update member contribution records
+ await UpdateMemberContributionRecords(allianceId, kingdomId, contributionTracking, researchNode);
+
+ _logger.LogInformation("Alliance research completed: Alliance {AllianceId}, {ResearchType}.{ResearchNode} Level {NewLevel}",
+ allianceId, researchType, researchNode, newLevel);
+
+ return (true, newLevel, unlockedBenefits, contributionTracking);
+ });
+ }
+
+ public async Task> ApplyCollectiveResearchBenefitsAsync(int allianceId, int kingdomId)
+ {
+ var alliance = await _allianceRepository.GetByIdAsync(allianceId, kingdomId);
+ if (alliance == null)
+ throw new ArgumentException($"Alliance {allianceId} not found");
+
+ var appliedBenefits = new Dictionary();
+
+ // Get all alliance members
+ var members = await _playerRepository.GetAllianceMembersAsync(allianceId, kingdomId);
+
+ // Calculate cumulative research benefits
+ var researchBonuses = CalculateCumulativeResearchBonuses(alliance.ResearchLevels);
+
+ appliedBenefits["MilitaryBranch"] = new Dictionary
+ {
+ ["AttackBonus"] = researchBonuses.GetValueOrDefault("Attack", 0),
+ ["DefenseBonus"] = researchBonuses.GetValueOrDefault("Defense", 0),
+ ["TroopCapacityBonus"] = researchBonuses.GetValueOrDefault("TroopCapacity", 0),
+ ["MarchSpeedBonus"] = researchBonuses.GetValueOrDefault("MarchSpeed", 0)
+ };
+
+ appliedBenefits["EconomicBranch"] = new Dictionary
+ {
+ ["ResourceProductionBonus"] = researchBonuses.GetValueOrDefault("ResourceProduction", 0),
+ ["ConstructionSpeedBonus"] = researchBonuses.GetValueOrDefault("ConstructionSpeed", 0),
+ ["ResourceCapacityBonus"] = researchBonuses.GetValueOrDefault("ResourceCapacity", 0),
+ ["TradingBonuses"] = researchBonuses.GetValueOrDefault("Trading", 0)
+ };
+
+ appliedBenefits["TechnologyBranch"] = new Dictionary
+ {
+ ["ResearchSpeedBonus"] = researchBonuses.GetValueOrDefault("ResearchSpeed", 0),
+ ["IntelligenceGatheringBonus"] = researchBonuses.GetValueOrDefault("Intelligence", 0),
+ ["CommunicationRangeBonus"] = researchBonuses.GetValueOrDefault("Communication", 0),
+ ["AdvancedFeatureUnlocks"] = GetUnlockedAdvancedFeatures(alliance.ResearchLevels)
+ };
+
+ appliedBenefits["MembersAffected"] = members.Count();
+ appliedBenefits["BenefitsAppliedAt"] = DateTime.UtcNow;
+
+ _logger.LogInformation("Collective research benefits applied: Alliance {AllianceId}, Members affected: {MemberCount}",
+ allianceId, members.Count());
+
+ return appliedBenefits;
+ }
+
+ public async Task<(bool CanResearch, List Prerequisites, Dictionary RequiredContributions,
+ Dictionary EstimatedBenefits)>
+ ValidateResearchRequirementsAsync(int allianceId, int kingdomId, string researchType, string researchNode)
+ {
+ var alliance = await _allianceRepository.GetByIdAsync(allianceId, kingdomId);
+ if (alliance == null)
+ return (false, new List { "Alliance not found" }, new Dictionary(),
+ new Dictionary());
+
+ var prerequisites = new List();
+ var requiredContributions = new Dictionary();
+
+ var currentLevel = alliance.ResearchLevels.GetValueOrDefault(researchNode, 0);
+ var targetLevel = currentLevel + 1;
+
+ // Check maximum research level
+ if (targetLevel > MAX_RESEARCH_LEVEL)
+ {
+ prerequisites.Add($"Maximum research level reached ({MAX_RESEARCH_LEVEL})");
+ }
+
+ // Check alliance level requirements
+ var requiredAllianceLevel = GetRequiredAllianceLevelForResearch(researchType, researchNode, targetLevel);
+ if (alliance.Level < requiredAllianceLevel)
+ {
+ prerequisites.Add($"Alliance level {requiredAllianceLevel} required (current: {alliance.Level})");
+ }
+
+ // Check prerequisite research nodes
+ var prerequisiteNodes = GetResearchPrerequisites(researchType, researchNode);
+ foreach (var prerequisite in prerequisiteNodes)
+ {
+ var prerequisiteLevel = alliance.ResearchLevels.GetValueOrDefault(prerequisite.Node, 0);
+ if (prerequisiteLevel < prerequisite.RequiredLevel)
+ {
+ prerequisites.Add($"{prerequisite.Node} level {prerequisite.RequiredLevel} required (current: {prerequisiteLevel})");
+ }
+ }
+
+ // Calculate resource requirements with exponential scaling
+ var baseCost = GetBaseResearchCost(researchType, researchNode);
+ var scalingFactor = Math.Pow(1.5, targetLevel - 1);
+
+ requiredContributions["Wood"] = (long)(baseCost * scalingFactor);
+ requiredContributions["Stone"] = (long)(baseCost * scalingFactor * 0.8);
+ requiredContributions["Iron"] = (long)(baseCost * scalingFactor * 0.6);
+ requiredContributions["Food"] = (long)(baseCost * scalingFactor * 1.2);
+ requiredContributions["Gold"] = (long)(baseCost * scalingFactor * 0.1);
+
+ // Calculate estimated benefits
+ var estimatedBenefits = CalculateResearchBenefits(researchType, researchNode, targetLevel);
+
+ var canResearch = prerequisites.Count == 0;
+
+ return (canResearch, prerequisites, requiredContributions, estimatedBenefits);
+ }
+
+ public async Task> TrackResearchContributionsAsync(int allianceId, int kingdomId,
+ int timeframeDays = 30)
+ {
+ var contributionTracking = new Dictionary();
+
+ // Get alliance members
+ var members = await _playerRepository.GetAllianceMembersAsync(allianceId, kingdomId);
+
+ // Get contribution records for timeframe
+ var contributionRecords = await GetMemberContributionRecords(allianceId, kingdomId, timeframeDays);
+
+ var memberContributions = new Dictionary();
+ var totalContributions = 0L;
+
+ foreach (var member in members)
+ {
+ var memberRecord = contributionRecords.ContainsKey(member.PlayerId) ?
+ contributionRecords[member.PlayerId] : new Dictionary();
+
+ var memberTotal = memberRecord.Values.Sum();
+ totalContributions += memberTotal;
+
+ memberContributions[$"Player_{member.PlayerId}"] = new Dictionary
+ {
+ ["PlayerName"] = member.PlayerName,
+ ["TotalContribution"] = memberTotal,
+ ["ResourceBreakdown"] = memberRecord,
+ ["ContributionPercentage"] = 0.0 // Will be calculated after total is known
+ };
+ }
+
+ // Calculate contribution percentages
+ foreach (var memberKey in memberContributions.Keys.ToList())
+ {
+ var memberData = (Dictionary)memberContributions[memberKey];
+ var memberTotal = (long)memberData["TotalContribution"];
+ var percentage = totalContributions > 0 ? (double)memberTotal / totalContributions * 100.0 : 0.0;
+ memberData["ContributionPercentage"] = percentage;
+ }
+
+ contributionTracking["TimeframeDays"] = timeframeDays;
+ contributionTracking["TotalContributions"] = totalContributions;
+ contributionTracking["MemberContributions"] = memberContributions;
+ contributionTracking["TopContributors"] = GetTopContributors(memberContributions, 5);
+ contributionTracking["ContributionDistribution"] = CalculateContributionDistribution(memberContributions);
+
+ return contributionTracking;
+ }
+
+ #endregion
+
+ #region Territory Management & Buildings
+
+ public async Task<(bool Success, string TerritoryId, Dictionary DefenseRequirements,
+ Dictionary TerritoryBenefits)>
+ ClaimTerritoryAsync(int allianceId, int kingdomId, (int X, int Y) territoryCoordinates, string territoryType)
+ {
+ _logger.LogInformation("Claiming territory for Alliance {AllianceId}: ({X}, {Y}) Type: {TerritoryType}",
+ allianceId, territoryCoordinates.X, territoryCoordinates.Y, territoryType);
+
+ return await _unitOfWork.ExecuteInTransactionAsync(async () =>
+ {
+ var alliance = await _allianceRepository.GetByIdAsync(allianceId, kingdomId);
+ if (alliance == null)
+ throw new ArgumentException($"Alliance {allianceId} not found");
+
+ // Validate territory claim requirements
+ var validation = await ValidateTerritoryClaim(allianceId, kingdomId, territoryCoordinates, territoryType);
+ if (!validation.CanClaim)
+ {
+ return (false, "", new Dictionary
+ {
+ ["Error"] = string.Join("; ", validation.BlockingReasons)
+ }, new Dictionary());
+ }
+
+ // Generate territory ID
+ var territoryId = $"TERRITORY_{allianceId}_{territoryCoordinates.X}_{territoryCoordinates.Y}";
+
+ // Calculate defense requirements
+ var defenseRequirements = CalculateTerritoryDefenseRequirements(territoryType, alliance.Level);
+
+ // Calculate territory benefits
+ var territoryBenefits = CalculateTerritoryBenefits(territoryType, territoryCoordinates);
+
+ // Create territory record
+ var territoryRecord = new Dictionary
+ {
+ ["TerritoryId"] = territoryId,
+ ["AllianceId"] = allianceId,
+ ["KingdomId"] = kingdomId,
+ ["Coordinates"] = territoryCoordinates,
+ ["TerritoryType"] = territoryType,
+ ["ClaimedAt"] = DateTime.UtcNow,
+ ["DefenseLevel"] = 1,
+ ["Buildings"] = new List