package serviceaccount

import (
	"context"
	"errors"
	"sort"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
	"github.com/tidbcloud/AgentTeam/internal/store/approval"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	serviceaccountstore "github.com/tidbcloud/AgentTeam/internal/store/serviceaccount"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

type CreateRequestDetail struct {
	Request               serviceaccountstore.CreateRequest
	CurrentProcessorNames []string
	ProcessorName         string
}

// AccountAccess is the caller-specific Service Account projection consumed by
// browser APIs. The durable account remains authoritative, while capabilities
// let the HTTP layer omit management-only identities and internal substrate
// from ordinary Reader responses.
type AccountAccess struct {
	Account           serviceaccountstore.Account
	CanManage         bool
	CanPublish        bool
	CanModerate       bool
	CanManagePlatform bool
}

// SubmitWorkspaceCreateRequestAndReconcile is the browser-management create
// boundary. Direct-create accounts are not exposed as ready until their
// authoritative Human audience has converged; approval-required requests stay
// pending for the Approval projector.
func (service *Service) SubmitWorkspaceCreateRequestAndReconcile(ctx context.Context, input SubmitWorkspaceCreateRequest) (WorkspaceCreateRequestResult, error) {
	result, err := service.SubmitWorkspaceCreateRequest(ctx, input)
	if err != nil || result.Account == nil {
		return result, err
	}
	account := *result.Account
	ready, err := service.ReconcileAllWorkspaceHumans(
		ctx,
		account.WorkspaceID,
		account.ID,
		account.Version,
		account.AudiencePolicyVersion,
		normalizedNow(input.Now),
	)
	if err != nil {
		return WorkspaceCreateRequestResult{}, err
	}
	request, err := service.accounts.LoadCreateRequest(ctx, result.Request.WorkspaceID, result.Request.ID)
	if err != nil {
		return WorkspaceCreateRequestResult{}, err
	}
	result.Request = request
	result.Account = &ready
	return result, nil
}

// ListAccountsForUser lists one bounded workspace service account page for an
// active Human.
func (service *Service) ListAccountsForUser(ctx context.Context, userID, workspaceID string, afterID string, query string, limit int) ([]AccountAccess, error) {
	if err := service.validate(); err != nil {
		return nil, err
	}
	membership, err := service.loadWorkspaceHuman(ctx, userID, workspaceID)
	if err != nil {
		return nil, err
	}
	// Catalog availability must not depend on historical cleanup succeeding.
	// The bounded, idempotent pass opportunistically withdraws approvals whose
	// requester has since become an owner/admin; a later read retries any
	// transient failure.
	_, _ = service.ReconcilePrivilegedWorkspaceCreateRequests(ctx, workspaceID, 100, normalizedNow(time.Time{}))
	accounts, err := service.accounts.ListAccountPage(ctx, workspaceID, afterID, query, limit)
	if err != nil {
		return nil, err
	}
	managedAccountIDs, err := service.accounts.ListActiveManagerAccountIDs(ctx, workspaceID, membership.Member.ID)
	if err != nil {
		return nil, err
	}
	managed := make(map[string]struct{}, len(managedAccountIDs))
	for _, accountID := range managedAccountIDs {
		managed[accountID] = struct{}{}
	}
	canManagePlatform := false
	for _, account := range accounts {
		if account.ManagementKind != serviceaccountstore.ManagementKindPlatform {
			continue
		}
		canManagePlatform, err = service.isSystemAdminUser(ctx, userID)
		if err != nil {
			return nil, err
		}
		break
	}
	result := make([]AccountAccess, 0, len(accounts))
	for _, account := range accounts {
		_, activeManager := managed[account.ID]
		result = append(result, projectAccountAccess(account, membership.WorkspaceRole, activeManager, canManagePlatform))
	}
	return result, nil
}

// GetAccountForUser returns one workspace service account to an active Human.
func (service *Service) GetAccountForUser(ctx context.Context, userID, workspaceID, accountID string) (AccountAccess, error) {
	if err := service.validate(); err != nil {
		return AccountAccess{}, err
	}
	membership, err := service.loadWorkspaceHuman(ctx, userID, workspaceID)
	if err != nil {
		return AccountAccess{}, err
	}
	account, err := service.accounts.LoadAccount(ctx, workspaceID, accountID)
	if err != nil {
		return AccountAccess{}, err
	}
	activeManager := false
	canManagePlatform := false
	if account.ManagementKind == serviceaccountstore.ManagementKindWorkspace {
		activeManager, err = service.accounts.HasActiveManager(ctx, workspaceID, accountID, membership.Member.ID)
	} else if account.ManagementKind == serviceaccountstore.ManagementKindPlatform {
		canManagePlatform, err = service.isSystemAdminUser(ctx, userID)
	}
	if err != nil {
		return AccountAccess{}, err
	}
	return projectAccountAccess(account, membership.WorkspaceRole, activeManager, canManagePlatform), nil
}

func projectAccountAccess(account serviceaccountstore.Account, workspaceRole string, activeManager bool, canManagePlatform bool) AccountAccess {
	workspaceManaged := account.ManagementKind == serviceaccountstore.ManagementKindWorkspace
	platformManaged := account.ManagementKind == serviceaccountstore.ManagementKindPlatform
	workspaceAdmin := access.HasWorkspaceAdminRole(domain.WorkspaceRole(workspaceRole))
	workspaceManager := activeManager || workspaceAdmin
	return AccountAccess{
		Account:   account,
		CanManage: workspaceManaged && workspaceManager,
		CanPublish: workspaceManaged && workspaceManager &&
			account.AudienceState == serviceaccountstore.AudienceStateReady &&
			account.EffectiveAudienceVersion == account.AudiencePolicyVersion,
		CanModerate:       (workspaceManaged && workspaceManager) || (platformManaged && canManagePlatform),
		CanManagePlatform: platformManaged && canManagePlatform,
	}
}

func (service *Service) isSystemAdminUser(ctx context.Context, userID string) (bool, error) {
	err := access.RequireSystemAdminUser(ctx, service.db, userID)
	if err == nil {
		return true, nil
	}
	if errors.Is(err, storeerr.ErrForbidden) || errors.Is(err, storeerr.ErrUnauthorized) {
		return false, nil
	}
	return false, err
}

// ListCreateRequestsForUser lists every request for owner/admin callers and
// only the authenticated requester's rows for ordinary members.
func (service *Service) ListCreateRequestsForUser(ctx context.Context, userID, workspaceID, state string, limit int) ([]serviceaccountstore.CreateRequest, error) {
	if err := service.validate(); err != nil {
		return nil, err
	}
	membership, err := service.loadWorkspaceHuman(ctx, userID, workspaceID)
	if err != nil {
		return nil, err
	}
	requesterMemberID := membership.Member.ID
	if access.HasWorkspaceAdminRole(domain.WorkspaceRole(membership.WorkspaceRole)) {
		requesterMemberID = ""
	}
	return service.accounts.ListCreateRequests(ctx, workspaceID, requesterMemberID, state, limit)
}

// GetCreateRequestForUser returns a request to its applicant or a current
// workspace owner/admin.
func (service *Service) GetCreateRequestForUser(ctx context.Context, userID, workspaceID, requestID string) (CreateRequestDetail, error) {
	if err := service.validate(); err != nil {
		return CreateRequestDetail{}, err
	}
	membership, err := service.loadWorkspaceHuman(ctx, userID, workspaceID)
	if err != nil {
		return CreateRequestDetail{}, err
	}
	request, err := service.accounts.LoadCreateRequest(ctx, workspaceID, requestID)
	if err != nil {
		return CreateRequestDetail{}, err
	}
	if request.RequestedByMemberID != membership.Member.ID &&
		!access.HasWorkspaceAdminRole(domain.WorkspaceRole(membership.WorkspaceRole)) {
		return CreateRequestDetail{}, storeerr.Forbidden("service account create request is not visible to this member")
	}
	detail := CreateRequestDetail{Request: request}
	if strings.TrimSpace(request.ApprovalInstanceID) == "" {
		return detail, nil
	}
	instance, err := approval.New(service.db).Load(ctx, workspaceID, request.ApprovalInstanceID)
	if err != nil {
		return CreateRequestDetail{}, err
	}
	memberIDs := make([]string, 0)
	if instance.State == approval.StatePending {
		assignments, err := approval.New(service.db).ListAssignments(ctx, workspaceID, instance.ID)
		if err != nil {
			return CreateRequestDetail{}, err
		}
		for _, assignment := range assignments {
			if assignment.Status == approval.AssignmentPending {
				memberIDs = append(memberIDs, assignment.ApproverMemberID)
			}
		}
	} else if strings.TrimSpace(instance.DecidedByMemberID) != "" {
		memberIDs = append(memberIDs, instance.DecidedByMemberID)
	}
	namesByID, err := identity.LoadPublicMemberNamesByMemberID(ctx, service.db, workspaceID, memberIDs)
	if err != nil {
		return CreateRequestDetail{}, err
	}
	if instance.State == approval.StatePending {
		seen := make(map[string]struct{})
		for _, memberID := range memberIDs {
			name := strings.TrimSpace(namesByID[memberID])
			if name == "" {
				continue
			}
			if _, exists := seen[name]; exists {
				continue
			}
			seen[name] = struct{}{}
			detail.CurrentProcessorNames = append(detail.CurrentProcessorNames, name)
		}
		sort.Strings(detail.CurrentProcessorNames)
	} else if len(memberIDs) == 1 {
		detail.ProcessorName = strings.TrimSpace(namesByID[memberIDs[0]])
	}
	return detail, nil
}

func (service *Service) loadWorkspaceHuman(ctx context.Context, userID, workspaceID string) (access.HumanWorkspaceMember, error) {
	return loadWorkspaceHuman(ctx, service.db, userID, workspaceID, false)
}

func (service *Service) loadWorkspaceHumanForUpdate(ctx context.Context, tx coredb.Tx, userID, workspaceID, expectedMemberID string) (access.HumanWorkspaceMember, error) {
	membership, err := loadWorkspaceHuman(ctx, tx, userID, workspaceID, true)
	if err != nil {
		return access.HumanWorkspaceMember{}, err
	}
	if strings.TrimSpace(expectedMemberID) == "" || strings.TrimSpace(membership.Member.ID) != strings.TrimSpace(expectedMemberID) {
		return access.HumanWorkspaceMember{}, storeerr.Forbidden("service account access requires the same active workspace Human")
	}
	return membership, nil
}

func loadWorkspaceHuman(ctx context.Context, queryer coredb.QueryRower, userID, workspaceID string, forUpdate bool) (access.HumanWorkspaceMember, error) {
	userID = strings.TrimSpace(userID)
	workspaceID = strings.TrimSpace(workspaceID)
	if userID == "" || workspaceID == "" {
		return access.HumanWorkspaceMember{}, storeerr.BadRequest("userId and workspaceId are required")
	}
	var (
		membership access.HumanWorkspaceMember
		err        error
	)
	if forUpdate {
		membership, err = access.LoadHumanWorkspaceMemberForUpdate(ctx, queryer, userID, workspaceID)
	} else {
		membership, err = access.LoadHumanWorkspaceMember(ctx, queryer, userID, workspaceID)
	}
	if err != nil {
		if errors.Is(err, access.ErrForbidden) || errors.Is(err, access.ErrNotFound) {
			return access.HumanWorkspaceMember{}, storeerr.Forbidden("service account access requires an active workspace Human")
		}
		return access.HumanWorkspaceMember{}, err
	}
	if strings.TrimSpace(membership.Member.ID) == "" ||
		strings.TrimSpace(membership.Member.WorkspaceID) != workspaceID ||
		membership.Member.UserID == nil || strings.TrimSpace(*membership.Member.UserID) != userID {
		return access.HumanWorkspaceMember{}, storeerr.Forbidden("service account access requires an active workspace Human")
	}
	return membership, nil
}
