package orgmembership

import (
	"context"
	"fmt"
	"strings"
	"time"

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

const orgMemberSourceLocal = "local"

type WorkspaceBackfillMemberParams struct {
	OrganizationID string
	UserID         string
	AuditUserID    string
}

type WorkspaceBackfillMemberResult struct {
	MemberID      string
	CreatedMember bool
	Skipped       bool
	SkipReason    string
}

// EnsureWorkspaceBackfillMemberInTx repairs historical active workspace members
// that predate the workspace-owned organization membership invariant.
//
// It intentionally does not reactivate inactive organization members. Those rows
// may represent an intentional suspension/removal and must be handled before the
// final invariant-enforcing rollout.
func EnsureWorkspaceBackfillMemberInTx(
	ctx context.Context,
	tx coredb.Tx,
	params WorkspaceBackfillMemberParams,
) (WorkspaceBackfillMemberResult, error) {
	organizationID := strings.TrimSpace(params.OrganizationID)
	userID := strings.TrimSpace(params.UserID)
	auditUserID := strings.TrimSpace(params.AuditUserID)
	if organizationID == "" {
		return WorkspaceBackfillMemberResult{}, storeerr.BadRequest("organizationId is required")
	}
	if userID == "" {
		return WorkspaceBackfillMemberResult{}, storeerr.BadRequest("userId is required")
	}
	if auditUserID == "" {
		return WorkspaceBackfillMemberResult{}, storeerr.BadRequest("auditUserId is required")
	}

	member, memberFound, err := loadOrganizationMemberByUser(ctx, tx, organizationID, userID)
	if err != nil {
		return WorkspaceBackfillMemberResult{}, err
	}

	now := time.Now().UTC()
	if !memberFound {
		return createWorkspaceBackfillOrganizationMember(ctx, tx, organizationID, userID, auditUserID, now)
	}
	if strings.TrimSpace(member.UserID) != userID {
		return WorkspaceBackfillMemberResult{
			MemberID:   member.ID,
			Skipped:    true,
			SkipReason: "organization_member_user_mismatch",
		}, nil
	}
	if member.Status != orgStatusActive {
		return WorkspaceBackfillMemberResult{
			MemberID:   member.ID,
			Skipped:    true,
			SkipReason: "organization_member_inactive",
		}, nil
	}
	return WorkspaceBackfillMemberResult{MemberID: member.ID}, nil
}

func createWorkspaceBackfillOrganizationMember(
	ctx context.Context,
	tx coredb.Tx,
	organizationID string,
	userID string,
	auditUserID string,
	now time.Time,
) (WorkspaceBackfillMemberResult, error) {
	defaultTeamID, err := loadDefaultTeamID(ctx, tx, organizationID)
	if err != nil {
		return WorkspaceBackfillMemberResult{}, err
	}
	defaultUnitID, err := loadDefaultUnitID(ctx, tx, organizationID)
	if err != nil {
		return WorkspaceBackfillMemberResult{}, err
	}

	memberID, err := insertWorkspaceBackfillOrganizationMember(ctx, tx, organizationID, userID, defaultTeamID, defaultUnitID, auditUserID, now)
	if err != nil {
		return WorkspaceBackfillMemberResult{}, err
	}
	if err := ensureWorkspaceBackfillDefaultMemberships(ctx, tx, organizationID, memberID, auditUserID, now, true); err != nil {
		return WorkspaceBackfillMemberResult{}, err
	}
	return WorkspaceBackfillMemberResult{
		MemberID:      memberID,
		CreatedMember: true,
	}, nil
}

func insertWorkspaceBackfillOrganizationMember(
	ctx context.Context,
	tx coredb.Tx,
	organizationID string,
	userID string,
	defaultTeamID string,
	defaultUnitID string,
	auditUserID string,
	now time.Time,
) (string, error) {
	memberID, err := identifiers.NewEntityID("orgm")
	if err != nil {
		return "", fmt.Errorf("generate organization member id: %w", err)
	}
	if _, err := tx.Exec(ctx, `
INSERT INTO organization_members (
    id,
    organization_id,
    user_id,
    primary_team_id,
    primary_unit_id,
    org_role,
    status,
    source,
    default_workspace_role,
    joined_at,
    invited_by_user_id,
    created_by_user_id,
    created_at,
    updated_at
) VALUES ($1, $2, $3, $4, $5, $6, 'active', $7, $8, $9, $10, $10, $9, $9)
`, memberID, organizationID, userID, defaultTeamID, defaultUnitID, orgRoleMember, orgMemberSourceLocal, defaultWorkspaceRoleMember, now, auditUserID); err != nil {
		return "", fmt.Errorf("insert organization member from workspace backfill: %w", err)
	}
	return memberID, nil
}

func ensureWorkspaceBackfillDefaultMemberships(
	ctx context.Context,
	tx coredb.Tx,
	organizationID string,
	memberID string,
	auditUserID string,
	now time.Time,
	makePrimary bool,
) error {
	defaultTeamID, err := loadDefaultTeamID(ctx, tx, organizationID)
	if err != nil {
		return err
	}
	if err := upsertTeamMembership(ctx, tx, organizationID, defaultTeamID, memberID, auditUserID, makePrimary, now); err != nil {
		return err
	}
	if makePrimary {
		if err := switchPrimaryTeam(ctx, tx, organizationID, memberID, defaultTeamID, now); err != nil {
			return err
		}
	}

	defaultUnitID, err := loadDefaultUnitID(ctx, tx, organizationID)
	if err != nil {
		return err
	}
	if err := upsertUnitMembership(ctx, tx, organizationID, defaultUnitID, memberID, auditUserID, makePrimary, now); err != nil {
		return err
	}
	if makePrimary {
		if err := switchPrimaryUnit(ctx, tx, organizationID, memberID, defaultUnitID, now); err != nil {
			return err
		}
	}
	return reconcileUnitBackedTeamMemberships(ctx, tx, organizationID, defaultUnitID, memberID, auditUserID, now)
}
