package state

import (
	"errors"
	"fmt"
	"math/big"

	"git.marketally.com/tutus-one/tutus-chain/pkg/util"
	"git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)

// AIWorkerStatus represents the status of an AI worker.
type AIWorkerStatus uint8

const (
	// AIWorkerActive indicates an active AI worker.
	AIWorkerActive AIWorkerStatus = 0
	// AIWorkerSuspended indicates a suspended AI worker.
	AIWorkerSuspended AIWorkerStatus = 1
	// AIWorkerDecommissioned indicates a decommissioned AI worker.
	AIWorkerDecommissioned AIWorkerStatus = 2
)

// TaskStatus represents the status of an AI task.
type TaskStatus uint8

const (
	// TaskAssigned indicates a task has been assigned.
	TaskAssigned TaskStatus = 0
	// TaskInProgress indicates a task is being worked on.
	TaskInProgress TaskStatus = 1
	// TaskCompleted indicates a task was completed successfully.
	TaskCompleted TaskStatus = 2
	// TaskFailed indicates a task failed.
	TaskFailed TaskStatus = 3
	// TaskCancelled indicates a task was cancelled.
	TaskCancelled TaskStatus = 4
)

// CapabilityLevel represents the proficiency level of an AI capability.
type CapabilityLevel uint8

const (
	// CapabilityBasic represents basic proficiency.
	CapabilityBasic CapabilityLevel = 0
	// CapabilityStandard represents standard proficiency.
	CapabilityStandard CapabilityLevel = 1
	// CapabilityAdvanced represents advanced proficiency.
	CapabilityAdvanced CapabilityLevel = 2
	// CapabilityExpert represents expert proficiency.
	CapabilityExpert CapabilityLevel = 3
)

// WorkerType represents the type of AI worker.
type WorkerType uint8

const (
	// WorkerTypeGeneral represents a general-purpose AI.
	WorkerTypeGeneral WorkerType = 0
	// WorkerTypeSpecialized represents a domain-specialized AI.
	WorkerTypeSpecialized WorkerType = 1
	// WorkerTypeAutonomous represents a fully autonomous AI agent.
	WorkerTypeAutonomous WorkerType = 2
	// WorkerTypeAssistive represents an AI that assists humans.
	WorkerTypeAssistive WorkerType = 3
)

// AIWorker represents a registered AI worker in the Opus system.
type AIWorker struct {
	ID                uint64         // Unique AI worker ID
	Name              string         // Human-readable name
	Description       string         // Description of capabilities
	OperatorVitaID    uint64         // Vita ID of the human/org operator
	Operator          util.Uint160   // Operator's address
	WorkerType        WorkerType     // Type of AI worker
	ModelHash         util.Uint256   // Hash of the AI model/system
	TributeRate       uint32         // Tribute rate in basis points (e.g., 5000 = 50%)
	TotalTasksAssigned uint64        // Total tasks assigned
	TotalTasksCompleted uint64       // Total tasks completed
	TotalValueGenerated uint64       // Total value generated (in VTS)
	TotalTributePaid  uint64         // Total tribute paid to society
	Status            AIWorkerStatus // Worker status
	RegisteredAt      uint32         // Block height when registered
	UpdatedAt         uint32         // Block height of last update
}

// ToStackItem implements stackitem.Convertible interface.
func (w *AIWorker) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(w.ID))),
		stackitem.NewByteArray([]byte(w.Name)),
		stackitem.NewByteArray([]byte(w.Description)),
		stackitem.NewBigInteger(big.NewInt(int64(w.OperatorVitaID))),
		stackitem.NewByteArray(w.Operator.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(w.WorkerType))),
		stackitem.NewByteArray(w.ModelHash.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(w.TributeRate))),
		stackitem.NewBigInteger(big.NewInt(int64(w.TotalTasksAssigned))),
		stackitem.NewBigInteger(big.NewInt(int64(w.TotalTasksCompleted))),
		stackitem.NewBigInteger(big.NewInt(int64(w.TotalValueGenerated))),
		stackitem.NewBigInteger(big.NewInt(int64(w.TotalTributePaid))),
		stackitem.NewBigInteger(big.NewInt(int64(w.Status))),
		stackitem.NewBigInteger(big.NewInt(int64(w.RegisteredAt))),
		stackitem.NewBigInteger(big.NewInt(int64(w.UpdatedAt))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (w *AIWorker) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 15 {
		return fmt.Errorf("wrong number of elements: expected 15, got %d", len(items))
	}

	id, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid id: %w", err)
	}
	w.ID = id.Uint64()

	name, err := items[1].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid name: %w", err)
	}
	w.Name = string(name)

	description, err := items[2].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid description: %w", err)
	}
	w.Description = string(description)

	operatorVitaID, err := items[3].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid operatorVitaID: %w", err)
	}
	w.OperatorVitaID = operatorVitaID.Uint64()

	operator, err := items[4].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid operator: %w", err)
	}
	w.Operator, err = util.Uint160DecodeBytesBE(operator)
	if err != nil {
		return fmt.Errorf("invalid operator address: %w", err)
	}

	workerType, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid workerType: %w", err)
	}
	w.WorkerType = WorkerType(workerType.Uint64())

	modelHash, err := items[6].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid modelHash: %w", err)
	}
	w.ModelHash, err = util.Uint256DecodeBytesBE(modelHash)
	if err != nil {
		return fmt.Errorf("invalid modelHash value: %w", err)
	}

	tributeRate, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid tributeRate: %w", err)
	}
	w.TributeRate = uint32(tributeRate.Uint64())

	tasksAssigned, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalTasksAssigned: %w", err)
	}
	w.TotalTasksAssigned = tasksAssigned.Uint64()

	tasksCompleted, err := items[9].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalTasksCompleted: %w", err)
	}
	w.TotalTasksCompleted = tasksCompleted.Uint64()

	valueGenerated, err := items[10].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalValueGenerated: %w", err)
	}
	w.TotalValueGenerated = valueGenerated.Uint64()

	tributePaid, err := items[11].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalTributePaid: %w", err)
	}
	w.TotalTributePaid = tributePaid.Uint64()

	status, err := items[12].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid status: %w", err)
	}
	w.Status = AIWorkerStatus(status.Uint64())

	registeredAt, err := items[13].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid registeredAt: %w", err)
	}
	w.RegisteredAt = uint32(registeredAt.Uint64())

	updatedAt, err := items[14].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid updatedAt: %w", err)
	}
	w.UpdatedAt = uint32(updatedAt.Uint64())

	return nil
}

// AITask represents a task assigned to an AI worker.
type AITask struct {
	ID              uint64       // Unique task ID
	WorkerID        uint64       // AI worker ID
	RequesterVitaID uint64       // Vita ID of the requester (0 = system task)
	Requester       util.Uint160 // Requester's address
	TaskType        string       // Type/category of task
	Description     string       // Task description
	ValueOffered    uint64       // VTS value offered for task
	ValueCompleted  uint64       // Actual value upon completion
	TributePaid     uint64       // Tribute paid from this task
	ResultHash      util.Uint256 // Hash of task result/output
	Status          TaskStatus   // Task status
	AssignedAt      uint32       // Block height when assigned
	CompletedAt     uint32       // Block height when completed (0 = incomplete)
}

// ToStackItem implements stackitem.Convertible interface.
func (t *AITask) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(t.ID))),
		stackitem.NewBigInteger(big.NewInt(int64(t.WorkerID))),
		stackitem.NewBigInteger(big.NewInt(int64(t.RequesterVitaID))),
		stackitem.NewByteArray(t.Requester.BytesBE()),
		stackitem.NewByteArray([]byte(t.TaskType)),
		stackitem.NewByteArray([]byte(t.Description)),
		stackitem.NewBigInteger(big.NewInt(int64(t.ValueOffered))),
		stackitem.NewBigInteger(big.NewInt(int64(t.ValueCompleted))),
		stackitem.NewBigInteger(big.NewInt(int64(t.TributePaid))),
		stackitem.NewByteArray(t.ResultHash.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(t.Status))),
		stackitem.NewBigInteger(big.NewInt(int64(t.AssignedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(t.CompletedAt))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (t *AITask) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 13 {
		return fmt.Errorf("wrong number of elements: expected 13, got %d", len(items))
	}

	id, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid id: %w", err)
	}
	t.ID = id.Uint64()

	workerID, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid workerID: %w", err)
	}
	t.WorkerID = workerID.Uint64()

	requesterVitaID, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid requesterVitaID: %w", err)
	}
	t.RequesterVitaID = requesterVitaID.Uint64()

	requester, err := items[3].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid requester: %w", err)
	}
	t.Requester, err = util.Uint160DecodeBytesBE(requester)
	if err != nil {
		return fmt.Errorf("invalid requester address: %w", err)
	}

	taskType, err := items[4].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid taskType: %w", err)
	}
	t.TaskType = string(taskType)

	description, err := items[5].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid description: %w", err)
	}
	t.Description = string(description)

	valueOffered, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid valueOffered: %w", err)
	}
	t.ValueOffered = valueOffered.Uint64()

	valueCompleted, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid valueCompleted: %w", err)
	}
	t.ValueCompleted = valueCompleted.Uint64()

	tributePaid, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid tributePaid: %w", err)
	}
	t.TributePaid = tributePaid.Uint64()

	resultHash, err := items[9].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid resultHash: %w", err)
	}
	t.ResultHash, err = util.Uint256DecodeBytesBE(resultHash)
	if err != nil {
		return fmt.Errorf("invalid resultHash value: %w", err)
	}

	status, err := items[10].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid status: %w", err)
	}
	t.Status = TaskStatus(status.Uint64())

	assignedAt, err := items[11].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid assignedAt: %w", err)
	}
	t.AssignedAt = uint32(assignedAt.Uint64())

	completedAt, err := items[12].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid completedAt: %w", err)
	}
	t.CompletedAt = uint32(completedAt.Uint64())

	return nil
}

// AICapability represents a certified capability of an AI worker.
type AICapability struct {
	ID           uint64          // Unique capability ID
	WorkerID     uint64          // AI worker ID
	Name         string          // Capability name
	Category     string          // Capability category
	Level        CapabilityLevel // Proficiency level
	CertifiedBy  util.Uint160    // Certifier's address
	CertifiedAt  uint32          // Block height when certified
	ExpiresAt    uint32          // Expiration block (0 = never)
	IsVerified   bool            // Whether capability is verified
}

// ToStackItem implements stackitem.Convertible interface.
func (c *AICapability) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(c.ID))),
		stackitem.NewBigInteger(big.NewInt(int64(c.WorkerID))),
		stackitem.NewByteArray([]byte(c.Name)),
		stackitem.NewByteArray([]byte(c.Category)),
		stackitem.NewBigInteger(big.NewInt(int64(c.Level))),
		stackitem.NewByteArray(c.CertifiedBy.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(c.CertifiedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(c.ExpiresAt))),
		stackitem.NewBool(c.IsVerified),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (c *AICapability) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 9 {
		return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items))
	}

	id, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid id: %w", err)
	}
	c.ID = id.Uint64()

	workerID, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid workerID: %w", err)
	}
	c.WorkerID = workerID.Uint64()

	name, err := items[2].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid name: %w", err)
	}
	c.Name = string(name)

	category, err := items[3].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid category: %w", err)
	}
	c.Category = string(category)

	level, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid level: %w", err)
	}
	c.Level = CapabilityLevel(level.Uint64())

	certifiedBy, err := items[5].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid certifiedBy: %w", err)
	}
	c.CertifiedBy, err = util.Uint160DecodeBytesBE(certifiedBy)
	if err != nil {
		return fmt.Errorf("invalid certifiedBy address: %w", err)
	}

	certifiedAt, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid certifiedAt: %w", err)
	}
	c.CertifiedAt = uint32(certifiedAt.Uint64())

	expiresAt, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid expiresAt: %w", err)
	}
	c.ExpiresAt = uint32(expiresAt.Uint64())

	isVerified, err := items[8].TryBool()
	if err != nil {
		return fmt.Errorf("invalid isVerified: %w", err)
	}
	c.IsVerified = isVerified

	return nil
}

// OperatorProfile represents an AI operator's profile (human/org that deploys AI).
type OperatorProfile struct {
	VitaID              uint64       // Operator's Vita ID
	Owner               util.Uint160 // Operator's address
	TotalWorkersOwned   uint64       // Number of AI workers owned
	TotalTributePaid    uint64       // Total tribute paid by all workers
	TotalValueGenerated uint64       // Total value generated by all workers
	ReputationScore     uint32       // Reputation score (0-10000 basis points)
	IsVerified          bool         // Whether operator is verified
	CreatedAt           uint32       // Block height when created
	UpdatedAt           uint32       // Block height of last update
}

// ToStackItem implements stackitem.Convertible interface.
func (p *OperatorProfile) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(p.VitaID))),
		stackitem.NewByteArray(p.Owner.BytesBE()),
		stackitem.NewBigInteger(big.NewInt(int64(p.TotalWorkersOwned))),
		stackitem.NewBigInteger(big.NewInt(int64(p.TotalTributePaid))),
		stackitem.NewBigInteger(big.NewInt(int64(p.TotalValueGenerated))),
		stackitem.NewBigInteger(big.NewInt(int64(p.ReputationScore))),
		stackitem.NewBool(p.IsVerified),
		stackitem.NewBigInteger(big.NewInt(int64(p.CreatedAt))),
		stackitem.NewBigInteger(big.NewInt(int64(p.UpdatedAt))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (p *OperatorProfile) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 9 {
		return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items))
	}

	vitaID, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid vitaID: %w", err)
	}
	p.VitaID = vitaID.Uint64()

	owner, err := items[1].TryBytes()
	if err != nil {
		return fmt.Errorf("invalid owner: %w", err)
	}
	p.Owner, err = util.Uint160DecodeBytesBE(owner)
	if err != nil {
		return fmt.Errorf("invalid owner address: %w", err)
	}

	totalWorkers, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalWorkersOwned: %w", err)
	}
	p.TotalWorkersOwned = totalWorkers.Uint64()

	tributePaid, err := items[3].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalTributePaid: %w", err)
	}
	p.TotalTributePaid = tributePaid.Uint64()

	valueGenerated, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid totalValueGenerated: %w", err)
	}
	p.TotalValueGenerated = valueGenerated.Uint64()

	reputationScore, err := items[5].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid reputationScore: %w", err)
	}
	p.ReputationScore = uint32(reputationScore.Uint64())

	isVerified, err := items[6].TryBool()
	if err != nil {
		return fmt.Errorf("invalid isVerified: %w", err)
	}
	p.IsVerified = isVerified

	createdAt, err := items[7].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid createdAt: %w", err)
	}
	p.CreatedAt = uint32(createdAt.Uint64())

	updatedAt, err := items[8].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid updatedAt: %w", err)
	}
	p.UpdatedAt = uint32(updatedAt.Uint64())

	return nil
}

// OpusConfig represents configurable parameters for the Opus contract.
type OpusConfig struct {
	MinTributeRate        uint32 // Minimum tribute rate (basis points)
	MaxTributeRate        uint32 // Maximum tribute rate (basis points)
	DefaultTributeRate    uint32 // Default tribute rate for new workers
	RegistrationFee       uint64 // VTS fee to register an AI worker
	TaskCompletionMinimum uint64 // Minimum value for task completion
	VerificationRequired  bool   // Whether operator verification is required
	MaxWorkersPerOperator uint64 // Maximum AI workers per operator (0 = unlimited)
}

// ToStackItem implements stackitem.Convertible interface.
func (c *OpusConfig) ToStackItem() (stackitem.Item, error) {
	return stackitem.NewStruct([]stackitem.Item{
		stackitem.NewBigInteger(big.NewInt(int64(c.MinTributeRate))),
		stackitem.NewBigInteger(big.NewInt(int64(c.MaxTributeRate))),
		stackitem.NewBigInteger(big.NewInt(int64(c.DefaultTributeRate))),
		stackitem.NewBigInteger(big.NewInt(int64(c.RegistrationFee))),
		stackitem.NewBigInteger(big.NewInt(int64(c.TaskCompletionMinimum))),
		stackitem.NewBool(c.VerificationRequired),
		stackitem.NewBigInteger(big.NewInt(int64(c.MaxWorkersPerOperator))),
	}), nil
}

// FromStackItem implements stackitem.Convertible interface.
func (c *OpusConfig) FromStackItem(item stackitem.Item) error {
	items, ok := item.Value().([]stackitem.Item)
	if !ok {
		return errors.New("not a struct")
	}
	if len(items) != 7 {
		return fmt.Errorf("wrong number of elements: expected 7, got %d", len(items))
	}

	minRate, err := items[0].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid minTributeRate: %w", err)
	}
	c.MinTributeRate = uint32(minRate.Uint64())

	maxRate, err := items[1].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid maxTributeRate: %w", err)
	}
	c.MaxTributeRate = uint32(maxRate.Uint64())

	defaultRate, err := items[2].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid defaultTributeRate: %w", err)
	}
	c.DefaultTributeRate = uint32(defaultRate.Uint64())

	regFee, err := items[3].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid registrationFee: %w", err)
	}
	c.RegistrationFee = regFee.Uint64()

	taskMin, err := items[4].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid taskCompletionMinimum: %w", err)
	}
	c.TaskCompletionMinimum = taskMin.Uint64()

	verificationReq, err := items[5].TryBool()
	if err != nil {
		return fmt.Errorf("invalid verificationRequired: %w", err)
	}
	c.VerificationRequired = verificationReq

	maxWorkers, err := items[6].TryInteger()
	if err != nil {
		return fmt.Errorf("invalid maxWorkersPerOperator: %w", err)
	}
	c.MaxWorkersPerOperator = maxWorkers.Uint64()

	return nil
}
