package quests

import "fmt"

// Player interface defines the required player functionality for quest system
type Player interface {
	// Basic player information
	GetID() int32
	GetName() string
	GetLevel() int8
	GetTSLevel() int8
	GetClass() int8
	GetTSClass() int8
	GetRace() int8

	// Client interface for packet sending
	GetClient() Client

	// Quest-related player methods
	GetQuest(questID int32) *Quest
	HasQuest(questID int32) bool
	HasQuestBeenCompleted(questID int32) bool
	AddQuest(quest *Quest) error
	RemoveQuest(questID int32) bool

	// Position and zone information
	GetX() float32
	GetY() float32
	GetZ() float32
	GetZoneID() int32

	// Faction information
	GetFactionValue(factionID int32) int32

	// Experience and rewards
	AddCoins(amount int64)
	AddExp(amount int32)
	AddTSExp(amount int32)
	AddStatusPoints(amount int32)

	// Inventory management
	GetPlayerItemList() ItemList

	// Arrow color calculation (difficulty indication)
	GetArrowColor(level int8) int8
	GetTSArrowColor(level int8) int8
}

// Client interface defines client functionality needed for quest system
type Client interface {
	// Basic client information
	GetVersion() int16
	GetNameCRC() int32
	GetPlayer() Player

	// Packet sending
	QueuePacket(packet Packet)
	SimpleMessage(color int32, message string)

	// Quest-specific client methods
	SendQuestJournalUpdate(quest *Quest, forceUpdate bool)
	PopulateQuestRewardItems(items *[]Item, packet PacketStruct, arrayName ...string)
}

// Spawn interface defines spawn functionality needed for quest system
type Spawn interface {
	// Basic spawn information
	GetID() int32
	GetDatabaseID() int32
	GetName() string
	GetRace() int8
	GetModelType() int16

	// Position information
	GetX() float32
	GetY() float32
	GetZ() float32
	GetZoneID() int32
}

// Spell interface defines spell functionality needed for quest system
type Spell interface {
	GetSpellID() int32
	GetName() string
}

// Item interface defines item functionality needed for quest system
type Item interface {
	GetItemID() int32
	GetName() string
	GetIcon(version int16) int32
	GetDetails() ItemDetails
	GetStackCount() int32
}

// ItemDetails interface defines item detail functionality
type ItemDetails interface {
	GetItemID() int32
	GetUniqueID() int32
	GetCount() int32
}

// ItemList interface defines item list functionality
type ItemList interface {
	GetItemFromID(itemID int32, count int32) Item
}

// Packet interface defines packet functionality
type Packet interface {
	// Packet data methods (placeholder)
	Serialize() []byte
}

// PacketStruct interface defines packet structure functionality
type PacketStruct interface {
	// Packet building methods
	SetDataByName(name string, value interface{}, index ...int)
	SetArrayLengthByName(name string, length int)
	SetArrayDataByName(name string, value interface{}, index int)
	SetSubArrayLengthByName(name string, length int, index int)
	SetSubArrayDataByName(name string, value interface{}, index1, index2 int)
	SetSubstructArrayDataByName(substruct, name string, value interface{}, index int)
	SetItemArrayDataByName(name string, item Item, player Player, index int, flag ...int)
	Serialize() Packet
	GetVersion() int16
}

// QuestOfferer interface defines NPCs that can offer quests
type QuestOfferer interface {
	Spawn
	GetOfferedQuests() []int32
	CanOfferQuest(questID int32, player Player) bool
	OfferQuest(questID int32, player Player) error
}

// QuestReceiver interface defines NPCs that can receive completed quests
type QuestReceiver interface {
	Spawn
	GetAcceptedQuests() []int32
	CanAcceptQuest(questID int32, player Player) bool
	AcceptCompletedQuest(questID int32, player Player) error
}

// QuestValidator interface defines quest validation functionality
type QuestValidator interface {
	ValidatePrerequisites(quest *Quest, player Player) error
	ValidateQuestCompletion(quest *Quest, player Player) error
	ValidateQuestSharing(quest *Quest, sharer, receiver Player) error
}

// QuestRewardProcessor interface defines quest reward processing
type QuestRewardProcessor interface {
	ProcessQuestRewards(quest *Quest, player Player) error
	CalculateRewards(quest *Quest, player Player) *QuestRewards
	GiveRewards(rewards *QuestRewards, player Player) error
}

// QuestRewards contains calculated quest rewards
type QuestRewards struct {
	Coins          int64           `json:"coins"`
	Experience     int32           `json:"experience"`
	TSExperience   int32           `json:"ts_experience"`
	StatusPoints   int32           `json:"status_points"`
	Items          []Item          `json:"items"`
	FactionRewards map[int32]int32 `json:"faction_rewards"`
}

// QuestEventHandler interface defines quest event handling
type QuestEventHandler interface {
	OnQuestStarted(quest *Quest, player Player)
	OnQuestStepCompleted(quest *Quest, step *QuestStep, player Player)
	OnQuestCompleted(quest *Quest, player Player)
	OnQuestFailed(quest *Quest, step *QuestStep, player Player)
	OnQuestAbandoned(quest *Quest, player Player)
	OnQuestShared(quest *Quest, sharer, receiver Player)
}

// Database interface defines database operations for quest system
type Database interface {
	// Quest CRUD operations
	LoadQuest(questID int32) (*Quest, error)
	SaveQuest(quest *Quest) error
	DeleteQuest(questID int32) error
	LoadAllQuests() ([]*Quest, error)

	// Player quest operations
	LoadPlayerQuests(playerID int32) ([]*Quest, error)
	SavePlayerQuest(playerID int32, quest *Quest) error
	DeletePlayerQuest(playerID, questID int32) error
	MarkQuestCompleted(playerID, questID int32) error
	IsQuestCompleted(playerID, questID int32) bool

	// Quest step operations
	SaveQuestStepProgress(playerID, questID, stepID int32, progress int32) error
	LoadQuestStepProgress(playerID, questID, stepID int32) (int32, error)
}

// Logger interface defines logging functionality for quest system
type Logger interface {
	LogInfo(message string, args ...interface{})
	LogError(message string, args ...interface{})
	LogDebug(message string, args ...interface{})
	LogWarning(message string, args ...interface{})
}

// Configuration interface defines quest system configuration
type Configuration interface {
	GetMaxPlayerQuests() int
	GetQuestSharingEnabled() bool
	GetQuestFailureEnabled() bool
	GetQuestTimersEnabled() bool
	GetXPBonusMultiplier() float64
	GetCoinBonusMultiplier() float64
	GetStatusBonusMultiplier() float64
}

// QuestSystemAdapter provides integration with other game systems
type QuestSystemAdapter struct {
	questManager    *QuestManager
	validator       QuestValidator
	rewardProcessor QuestRewardProcessor
	eventHandler    QuestEventHandler
	database        Database
	logger          Logger
	config          Configuration
}

// NewQuestSystemAdapter creates a new quest system adapter
func NewQuestSystemAdapter(questManager *QuestManager, validator QuestValidator, rewardProcessor QuestRewardProcessor, eventHandler QuestEventHandler, database Database, logger Logger, config Configuration) *QuestSystemAdapter {
	return &QuestSystemAdapter{
		questManager:    questManager,
		validator:       validator,
		rewardProcessor: rewardProcessor,
		eventHandler:    eventHandler,
		database:        database,
		logger:          logger,
		config:          config,
	}
}

// StartQuest handles starting a new quest for a player
func (qsa *QuestSystemAdapter) StartQuest(questID int32, player Player) error {
	// Get quest from master list
	quest := qsa.questManager.GetMasterList().GetQuest(questID, true)
	if quest == nil {
		return fmt.Errorf("quest %d not found", questID)
	}

	// Validate prerequisites
	if qsa.validator != nil {
		if err := qsa.validator.ValidatePrerequisites(quest, player); err != nil {
			return fmt.Errorf("quest prerequisites not met: %w", err)
		}
	}

	// Check if player can accept more quests
	if qsa.config != nil {
		maxQuests := qsa.config.GetMaxPlayerQuests()
		if maxQuests > 0 && qsa.questManager.GetPlayerQuestCount(player.GetID()) >= maxQuests {
			return fmt.Errorf("player has too many active quests")
		}
	}

	// Give quest to player
	_, err := qsa.questManager.GiveQuestToPlayer(player.GetID(), questID)
	if err != nil {
		return err
	}

	// Save to database
	if qsa.database != nil {
		if err := qsa.database.SavePlayerQuest(player.GetID(), quest); err != nil {
			qsa.logger.LogError("Failed to save player quest to database: %v", err)
		}
	}

	// Trigger event
	if qsa.eventHandler != nil {
		qsa.eventHandler.OnQuestStarted(quest, player)
	}

	// Log
	if qsa.logger != nil {
		qsa.logger.LogInfo("Player %s (%d) started quest %s (%d)", player.GetName(), player.GetID(), quest.Name, questID)
	}

	return nil
}

// CompleteQuest handles completing a quest for a player
func (qsa *QuestSystemAdapter) CompleteQuest(questID int32, player Player) error {
	// Get player's quest
	quest := qsa.questManager.GetPlayerQuest(player.GetID(), questID)
	if quest == nil {
		return fmt.Errorf("player does not have quest %d", questID)
	}

	// Validate completion
	if qsa.validator != nil {
		if err := qsa.validator.ValidateQuestCompletion(quest, player); err != nil {
			return fmt.Errorf("quest completion validation failed: %w", err)
		}
	}

	// Check if quest is actually complete
	if !quest.GetCompleted() {
		return fmt.Errorf("quest is not complete")
	}

	// Process rewards
	if qsa.rewardProcessor != nil {
		if err := qsa.rewardProcessor.ProcessQuestRewards(quest, player); err != nil {
			qsa.logger.LogError("Failed to process quest rewards: %v", err)
			return fmt.Errorf("failed to process quest rewards: %w", err)
		}
	}

	// Mark as completed in database
	if qsa.database != nil {
		if err := qsa.database.MarkQuestCompleted(player.GetID(), questID); err != nil {
			qsa.logger.LogError("Failed to mark quest as completed in database: %v", err)
		}
	}

	// Remove from active quests if not repeatable
	if !quest.IsRepeatable() {
		qsa.questManager.RemoveQuestFromPlayer(player.GetID(), questID)
	}

	// Trigger event
	if qsa.eventHandler != nil {
		qsa.eventHandler.OnQuestCompleted(quest, player)
	}

	// Log
	if qsa.logger != nil {
		qsa.logger.LogInfo("Player %s (%d) completed quest %s (%d)", player.GetName(), player.GetID(), quest.Name, questID)
	}

	return nil
}

// UpdateQuestProgress handles updating quest step progress
func (qsa *QuestSystemAdapter) UpdateQuestProgress(questID, stepID, progress int32, player Player) error {
	// Get player's quest
	quest := qsa.questManager.GetPlayerQuest(player.GetID(), questID)
	if quest == nil {
		return fmt.Errorf("player does not have quest %d", questID)
	}

	// Update progress
	if !quest.AddStepProgress(stepID, progress) {
		return fmt.Errorf("failed to update quest step progress")
	}

	// Check if step is now complete
	step := quest.GetQuestStep(stepID)
	if step != nil && step.Complete() {
		// Execute complete action if exists
		if quest.HasCompleteAction(stepID) {
			if err := quest.ExecuteCompleteAction(stepID); err != nil {
				qsa.logger.LogError("Failed to execute quest step complete action: %v", err)
			}
		}

		// Trigger event
		if qsa.eventHandler != nil {
			qsa.eventHandler.OnQuestStepCompleted(quest, step, player)
		}

		// Log
		if qsa.logger != nil {
			qsa.logger.LogInfo("Player %s (%d) completed step %d of quest %s (%d)", player.GetName(), player.GetID(), stepID, quest.Name, questID)
		}
	}

	// Save progress to database
	if qsa.database != nil {
		if err := qsa.database.SaveQuestStepProgress(player.GetID(), questID, stepID, step.GetStepProgress()); err != nil {
			qsa.logger.LogError("Failed to save quest step progress to database: %v", err)
		}
	}

	return nil
}

// AbandonQuest handles abandoning a quest
func (qsa *QuestSystemAdapter) AbandonQuest(questID int32, player Player) error {
	// Get player's quest
	quest := qsa.questManager.GetPlayerQuest(player.GetID(), questID)
	if quest == nil {
		return fmt.Errorf("player does not have quest %d", questID)
	}

	// Check if quest can be abandoned
	if !quest.CanDeleteQuest {
		return fmt.Errorf("quest cannot be abandoned")
	}

	// Remove from player
	if !qsa.questManager.RemoveQuestFromPlayer(player.GetID(), questID) {
		return fmt.Errorf("failed to remove quest from player")
	}

	// Remove from database
	if qsa.database != nil {
		if err := qsa.database.DeletePlayerQuest(player.GetID(), questID); err != nil {
			qsa.logger.LogError("Failed to delete player quest from database: %v", err)
		}
	}

	// Trigger event
	if qsa.eventHandler != nil {
		qsa.eventHandler.OnQuestAbandoned(quest, player)
	}

	// Log
	if qsa.logger != nil {
		qsa.logger.LogInfo("Player %s (%d) abandoned quest %s (%d)", player.GetName(), player.GetID(), quest.Name, questID)
	}

	return nil
}

// ShareQuest handles sharing a quest between players
func (qsa *QuestSystemAdapter) ShareQuest(questID int32, sharer, receiver Player) error {
	if !qsa.config.GetQuestSharingEnabled() {
		return fmt.Errorf("quest sharing is disabled")
	}

	// Get sharer's quest
	quest := qsa.questManager.GetPlayerQuest(sharer.GetID(), questID)
	if quest == nil {
		return fmt.Errorf("sharer does not have quest %d", questID)
	}

	// Validate sharing
	if qsa.validator != nil {
		if err := qsa.validator.ValidateQuestSharing(quest, sharer, receiver); err != nil {
			return fmt.Errorf("quest sharing validation failed: %w", err)
		}
	}

	// Check if receiver can accept more quests
	if qsa.config != nil {
		maxQuests := qsa.config.GetMaxPlayerQuests()
		if maxQuests > 0 && qsa.questManager.GetPlayerQuestCount(receiver.GetID()) >= maxQuests {
			return fmt.Errorf("receiver has too many active quests")
		}
	}

	// Give quest to receiver
	_, err := qsa.questManager.GiveQuestToPlayer(receiver.GetID(), questID)
	if err != nil {
		return fmt.Errorf("failed to give quest to receiver: %w", err)
	}

	// Save to database
	if qsa.database != nil {
		receiverQuest := qsa.questManager.GetPlayerQuest(receiver.GetID(), questID)
		if err := qsa.database.SavePlayerQuest(receiver.GetID(), receiverQuest); err != nil {
			qsa.logger.LogError("Failed to save shared quest to database: %v", err)
		}
	}

	// Trigger event
	if qsa.eventHandler != nil {
		qsa.eventHandler.OnQuestShared(quest, sharer, receiver)
	}

	// Log
	if qsa.logger != nil {
		qsa.logger.LogInfo("Player %s (%d) shared quest %s (%d) with %s (%d)",
			sharer.GetName(), sharer.GetID(), quest.Name, questID, receiver.GetName(), receiver.GetID())
	}

	return nil
}
