// Package team owns workspace Team persistence and low-level Team member
// lifecycle invariants. API authorization and mention delivery expansion live in
// higher layers that depend on this package.
package team

import (
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

var (
	ErrNotFound   = storeerr.ErrNotFound
	ErrConflict   = storeerr.ErrConflict
	ErrBadRequest = storeerr.ErrBadRequest
)

const (
	VisibilityWorkspace = "workspace"
	VisibilityPrivate   = "private"

	StatusActive   = "active"
	StatusArchived = "archived"

	ActorKindHuman = "human"
	ActorKindAgent = "agent"

	MemberRoleLeader = "leader"
	MemberRoleMember = "member"
)

// Store owns workspace Team data access.
type Store struct {
	db coredb.DB
}

// New constructs a Team Store.
func New(db coredb.DB) *Store {
	return &Store{db: db}
}

// Validate checks whether the store is configured.
func (store *Store) Validate() error {
	if store == nil || store.db == nil {
		return fmt.Errorf("team store database is not configured")
	}
	return nil
}

// InTx returns a transaction-bound Team store.
func (store *Store) InTx(tx coredb.Tx) *TxStore {
	return &TxStore{db: tx}
}

// TxStore exposes Team persistence helpers bound to an existing transaction.
type TxStore struct {
	db coredb.QueryExecer
}

func queryerFromStore(store *Store) (coredb.Queryer, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("team store database is not configured")
	}
	return store.db, nil
}

func execerFromStore(store *Store) (coredb.QueryExecer, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("team store database is not configured")
	}
	return store.db, nil
}

func txBeginnerFromStore(store *Store) (coredb.TxBeginner, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("team store database is not configured")
	}
	return store.db, nil
}

func queryerFromTxStore(store *TxStore) (coredb.Queryer, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("team tx store database is not configured")
	}
	return store.db, nil
}

func execerFromTxStore(store *TxStore) (coredb.QueryExecer, error) {
	if store == nil || store.db == nil {
		return nil, fmt.Errorf("team tx store database is not configured")
	}
	return store.db, nil
}

func trim(value string) string { return strings.TrimSpace(value) }

func badRequest(message string) error { return storeerr.BadRequest(message) }
func conflict(message string) error   { return storeerr.Conflict(message) }
func forbidden(message string) error  { return storeerr.Forbidden(message) }
func notFound(message string) error   { return storeerr.NotFound(message) }

func normalizeVisibility(value string) (string, error) {
	visibility := trim(value)
	if visibility == "" {
		return VisibilityPrivate, nil
	}
	switch visibility {
	case VisibilityWorkspace, VisibilityPrivate:
		return visibility, nil
	default:
		return "", badRequest("team visibility must be workspace or private")
	}
}

func normalizeActorKind(value string) (string, error) {
	kind := trim(value)
	switch kind {
	case ActorKindAgent:
		return kind, nil
	default:
		return "", badRequest("team members can only be agents")
	}
}

func activeHandleKey(workspaceID string, handle string, status string) any {
	if trim(status) != StatusActive {
		return nil
	}
	return trim(workspaceID) + ":" + strings.ToLower(trim(handle))
}

func activeMemberKey(teamID string, actorKind string, actorID string, removed bool) any {
	if removed {
		return nil
	}
	return trim(teamID) + ":" + trim(actorKind) + ":" + trim(actorID)
}

func activeAgentTeamKey(workspaceID string, actorKind string, actorID string, removed bool) any {
	if removed || trim(actorKind) != ActorKindAgent {
		return nil
	}
	return trim(workspaceID) + ":agent:" + trim(actorID)
}

func activeLeaderKey(workspaceID string, teamID string, actorKind string, role string, removed bool) any {
	if removed || trim(actorKind) != ActorKindAgent || trim(role) != MemberRoleLeader {
		return nil
	}
	return trim(workspaceID) + ":" + trim(teamID)
}

func mapTeamWriteError(err error) error {
	if err == nil {
		return nil
	}
	if constraint, ok := coredb.UniqueViolationConstraint(err); ok {
		switch constraint {
		case "workspace_teams_active_handle_key":
			return conflict("team handle is already in use")
		case "workspace_team_members_active_key":
			return conflict("team member already exists")
		case "workspace_team_members_active_agent_team_key":
			return conflict("agent already belongs to a team in this workspace")
		case "workspace_team_members_active_leader_key":
			return conflict("team already has a leader")
		}
	}
	if coredb.IsForeignKeyViolation(err) {
		return badRequest("team references an unknown workspace, member, or team")
	}
	return err
}
