package organization

import (
	"context"
	"database/sql"
	"fmt"
	"strings"
	"time"

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

const (
	organizationUnitStatusActive           = "active"
	organizationUnitMembershipSourceDirect = "direct"
	organizationUnitMembershipStatusActive = "active"
)

type organizationUnitRecord struct {
	ID               string
	ParentUnitID     string
	ManagerMemberID  string
	Name             string
	Slug             string
	Status           string
	ExternalProvider string
	ExternalID       string
	SyncedAt         *time.Time
	SyncMode         string
	SyncSource       string
	LastSyncRunID    string
	LastSyncRevision int64
	LastSyncStatus   string
	IsDefault        bool
}

type organizationMemberUnitMembershipRecord struct {
	ID        string
	MemberID  string
	UnitID    string
	Source    string
	Status    string
	IsPrimary bool
}

func (txs TxStore) ListOrganizationUnitsForUser(ctx context.Context, userID string, organizationID string) ([]OrganizationUnitView, error) {
	if _, err := txs.requireActiveOrganizationMembership(ctx, txs.db, userID, organizationID); err != nil {
		return nil, err
	}

	rows, err := txs.db.Query(ctx, `
SELECT ou.id,
       COALESCE(ou.parent_unit_id, ''),
       COALESCE(ou.manager_member_id, ''),
       ou.name,
       ou.slug,
       ou.status,
       ou.external_provider,
       ou.external_id,
       ou.synced_at,
       ou.sync_mode,
       ou.sync_source,
       COALESCE(osr.id, ''),
       COALESCE(osr.directory_revision, 0),
       COALESCE(osr.status, ''),
       ou.is_default,
       SUM(CASE WHEN om.status = 'active' THEN 1 ELSE 0 END)
FROM organization_units ou
LEFT JOIN organization_directory_sync_runs osr
  ON osr.id = ou.last_sync_run_id
LEFT JOIN organization_member_unit_memberships oaum
  ON oaum.organization_unit_id = ou.id
 AND oaum.status = 'active'
LEFT JOIN organization_members om
  ON om.id = oaum.organization_member_id
WHERE ou.organization_id = $1
GROUP BY ou.id, ou.parent_unit_id, ou.manager_member_id, ou.name, ou.slug, ou.status, ou.external_provider, ou.external_id, ou.synced_at, ou.sync_mode, ou.sync_source, osr.id, osr.directory_revision, osr.status, ou.is_default, ou.created_at
ORDER BY ou.is_default DESC, ou.created_at ASC, ou.id ASC
`, organizationID)
	if err != nil {
		return nil, fmt.Errorf("list organization units: %w", err)
	}
	defer rows.Close()

	units := make([]OrganizationUnitView, 0)
	for rows.Next() {
		unit, err := scanOrganizationUnitView(rows)
		if err != nil {
			return nil, err
		}
		units = append(units, unit)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate organization units: %w", err)
	}
	return units, nil
}

func (txs TxStore) CreateOrganizationUnitForUser(ctx context.Context, userID string, organizationID string, params CreateOrganizationUnitParams) (OrganizationUnitView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationUnitView{}, err
	}
	if err := txs.requireOrganizationLocalDirectoryWritesAllowed(ctx, txs.db, organizationID); err != nil {
		return OrganizationUnitView{}, err
	}

	trimmedName := strings.TrimSpace(params.Name)
	if trimmedName == "" {
		return OrganizationUnitView{}, badRequest("name is required")
	}

	resolvedSlug := strings.TrimSpace(params.Slug)
	if resolvedSlug == "" {
		resolvedSlug = slugifyOrganizationName(trimmedName)
	}
	if resolvedSlug == "" {
		return OrganizationUnitView{}, badRequest("slug is required")
	}
	if err := domain.ValidateWorkspaceSlug(resolvedSlug); err != nil {
		return OrganizationUnitView{}, err
	}

	return withTxValue(ctx, txs.db, func(tx coredb.Tx) (OrganizationUnitView, error) {
		parentUnitID := strings.TrimSpace(params.ParentUnitID)
		if parentUnitID != "" {
			parentUnit, err := txs.loadOrganizationUnit(ctx, tx, organizationID, parentUnitID)
			if err != nil {
				return OrganizationUnitView{}, err
			}
			if parentUnit.Status != organizationUnitStatusActive {
				return OrganizationUnitView{}, badRequest("parent unit is not active")
			}
		}

		managerAccountID := strings.TrimSpace(params.ManagerMemberID)
		if managerAccountID != "" {
			if _, err := txs.loadActiveOrganizationMember(ctx, tx, organizationID, managerAccountID); err != nil {
				return OrganizationUnitView{}, err
			}
		}

		unitID, err := identifiers.NewEntityID("orgu")
		if err != nil {
			return OrganizationUnitView{}, fmt.Errorf("generate organization unit id: %w", err)
		}
		now := time.Now().UTC()
		if _, err := tx.Exec(ctx, `
INSERT INTO organization_units (
    id,
    organization_id,
    parent_unit_id,
    manager_member_id,
    name,
    slug,
    status,
    external_provider,
    external_id,
    synced_at,
    is_default,
    created_by_user_id,
    created_at,
    updated_at
) VALUES ($1, $2, NULLIF($3, ''), NULLIF($4, ''), $5, $6, 'active', '', '', NULL, FALSE, $7, $8, $8)
`, unitID, organizationID, parentUnitID, managerAccountID, trimmedName, resolvedSlug, userID, now); err != nil {
			if isUniqueViolation(err) {
				return OrganizationUnitView{}, conflict("Unit slug already exists in organization.")
			}
			return OrganizationUnitView{}, fmt.Errorf("insert organization unit: %w", err)
		}

		unit, err := txs.loadOrganizationUnitView(ctx, tx, organizationID, unitID)
		if err != nil {
			return OrganizationUnitView{}, err
		}

		return unit, nil
	})
}

func (txs TxStore) ListOrganizationUnitMembersForUser(ctx context.Context, userID string, organizationID string, unitID string) ([]OrganizationUnitMemberView, error) {
	if _, err := txs.requireActiveOrganizationMembership(ctx, txs.db, userID, organizationID); err != nil {
		return nil, err
	}
	if _, err := txs.loadOrganizationUnit(ctx, txs.db, organizationID, unitID); err != nil {
		return nil, err
	}

	rows, err := txs.db.Query(ctx, `
SELECT oaum.organization_unit_id,
       om.id,
       u.id,
       COALESCE(u.email, ''),
       u.name,
       u.display_name,
       oaum.source,
       oaum.status,
       oaum.is_primary,
       COALESCE(pu.id, ''),
       COALESCE(pu.name, '')
FROM organization_member_unit_memberships oaum
JOIN organization_members om ON om.id = oaum.organization_member_id
JOIN users u ON u.id = om.user_id
LEFT JOIN organization_member_unit_memberships primary_membership
  ON primary_membership.organization_id = oaum.organization_id
 AND primary_membership.organization_member_id = oaum.organization_member_id
 AND primary_membership.status = 'active'
 AND primary_membership.is_primary = TRUE
LEFT JOIN organization_units pu ON pu.id = primary_membership.organization_unit_id
WHERE oaum.organization_id = $1
  AND oaum.organization_unit_id = $2
  AND oaum.status = 'active'
  AND om.status = 'active'
ORDER BY oaum.is_primary DESC, COALESCE(u.email, '') ASC, om.id ASC
`, organizationID, unitID)
	if err != nil {
		return nil, fmt.Errorf("list organization unit members: %w", err)
	}
	defer rows.Close()

	memberships := make([]OrganizationUnitMemberView, 0)
	for rows.Next() {
		var view OrganizationUnitMemberView
		if err := rows.Scan(
			&view.UnitID,
			&view.MemberID,
			&view.UserID,
			&view.Email,
			&view.Name,
			&view.DisplayName,
			&view.Source,
			&view.Status,
			&view.IsPrimary,
			&view.PrimaryUnitID,
			&view.PrimaryUnitName,
		); err != nil {
			return nil, fmt.Errorf("scan organization unit member: %w", err)
		}
		memberships = append(memberships, view)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate organization unit members: %w", err)
	}
	return memberships, nil
}

func (txs TxStore) AddOrganizationUnitMemberForUser(ctx context.Context, userID string, organizationID string, unitID string, params AddOrganizationUnitMemberParams) (OrganizationUnitMemberView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationUnitMemberView{}, err
	}
	if err := txs.requireOrganizationLocalDirectoryWritesAllowed(ctx, txs.db, organizationID); err != nil {
		return OrganizationUnitMemberView{}, err
	}

	accountID := strings.TrimSpace(params.MemberID)
	if accountID == "" {
		return OrganizationUnitMemberView{}, badRequest("memberId is required")
	}

	return withTxValue(ctx, txs.db, func(tx coredb.Tx) (OrganizationUnitMemberView, error) {
		unit, err := txs.loadOrganizationUnit(ctx, tx, organizationID, unitID)
		if err != nil {
			return OrganizationUnitMemberView{}, err
		}
		if err := requireLocallyMutableUnit(unit); err != nil {
			return OrganizationUnitMemberView{}, err
		}
		account, err := txs.loadActiveOrganizationMember(ctx, tx, organizationID, accountID)
		if err != nil {
			return OrganizationUnitMemberView{}, err
		}
		if err := requireLocallyMutableMember(account); err != nil {
			return OrganizationUnitMemberView{}, err
		}

		if err := txs.upsertOrganizationUnitMembership(ctx, tx, organizationID, unitID, accountID, userID, false); err != nil {
			return OrganizationUnitMemberView{}, err
		}
		if params.Primary {
			if err := txs.switchPrimaryUnitMembership(ctx, tx, organizationID, accountID, unitID); err != nil {
				return OrganizationUnitMemberView{}, err
			}
		}
		if err := txs.reconcileOrganizationUnitBackedTeamMemberships(ctx, tx, organizationID, unitID, userID); err != nil {
			return OrganizationUnitMemberView{}, err
		}

		view, err := txs.loadOrganizationUnitMemberView(ctx, tx, organizationID, unitID, accountID)
		if err != nil {
			return OrganizationUnitMemberView{}, err
		}

		return view, nil
	})
}

func (txs TxStore) RemoveOrganizationUnitMemberForUser(ctx context.Context, userID string, organizationID string, unitID string, accountID string) error {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return err
	}
	if err := txs.requireOrganizationLocalDirectoryWritesAllowed(ctx, txs.db, organizationID); err != nil {
		return err
	}

	return withTx(ctx, txs.db, func(tx coredb.Tx) error {
		unit, err := txs.loadOrganizationUnit(ctx, tx, organizationID, unitID)
		if err != nil {
			return err
		}
		if err := requireLocallyMutableUnit(unit); err != nil {
			return err
		}
		account, err := txs.loadActiveOrganizationMember(ctx, tx, organizationID, accountID)
		if err != nil {
			return err
		}
		if err := requireLocallyMutableMember(account); err != nil {
			return err
		}
		membership, err := txs.loadOrganizationUnitMembership(ctx, tx, organizationID, unitID, accountID)
		if err != nil {
			return err
		}
		if membership.IsPrimary {
			return badRequest("switch primary unit before removing this membership")
		}

		if _, err := tx.Exec(ctx, `
UPDATE organization_member_unit_memberships
SET status = 'inactive', updated_at = $4
WHERE organization_id = $1 AND organization_unit_id = $2 AND organization_member_id = $3
`, organizationID, unitID, accountID, time.Now().UTC()); err != nil {
			return fmt.Errorf("deactivate organization unit membership: %w", err)
		}
		if err := txs.reconcileOrganizationUnitBackedTeamMemberships(ctx, tx, organizationID, unitID, userID); err != nil {
			return err
		}

		return nil
	})
}

func (txs TxStore) ListOrganizationMemberUnitsForUser(ctx context.Context, userID string, organizationID string, accountID string) ([]OrganizationMemberUnitView, error) {
	if _, err := txs.requireActiveOrganizationMembership(ctx, txs.db, userID, organizationID); err != nil {
		return nil, err
	}
	if _, err := txs.loadActiveOrganizationMember(ctx, txs.db, organizationID, accountID); err != nil {
		return nil, err
	}

	rows, err := txs.db.Query(ctx, `
SELECT oaum.organization_member_id,
       oaum.organization_unit_id,
       ou.name,
       ou.slug,
       COALESCE(ou.parent_unit_id, ''),
       oaum.source,
       oaum.status,
       oaum.is_primary
FROM organization_member_unit_memberships oaum
JOIN organization_units ou ON ou.id = oaum.organization_unit_id
WHERE oaum.organization_id = $1
  AND oaum.organization_member_id = $2
  AND oaum.status = 'active'
ORDER BY oaum.is_primary DESC, ou.is_default DESC, ou.created_at ASC, ou.id ASC
`, organizationID, accountID)
	if err != nil {
		return nil, fmt.Errorf("list organization member units: %w", err)
	}
	defer rows.Close()

	units := make([]OrganizationMemberUnitView, 0)
	for rows.Next() {
		var view OrganizationMemberUnitView
		if err := rows.Scan(
			&view.MemberID,
			&view.UnitID,
			&view.UnitName,
			&view.UnitSlug,
			&view.ParentUnitID,
			&view.Source,
			&view.Status,
			&view.IsPrimary,
		); err != nil {
			return nil, fmt.Errorf("scan organization member unit: %w", err)
		}
		units = append(units, view)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate organization member units: %w", err)
	}
	return units, nil
}

func (txs TxStore) resolveOrganizationUnit(ctx context.Context, queryer queryRower, organizationID string, unitID string) (string, string, error) {
	trimmedUnitID := strings.TrimSpace(unitID)
	if trimmedUnitID == "" {
		row := queryer.QueryRow(ctx, `
SELECT id, name
FROM organization_units
WHERE organization_id = $1 AND is_default = TRUE
`, organizationID)
		var resolvedUnitID string
		var resolvedUnitName string
		if err := row.Scan(&resolvedUnitID, &resolvedUnitName); err != nil {
			if coredb.IsNotFound(err) {
				return "", "", ErrNotFound
			}
			return "", "", fmt.Errorf("load default organization unit: %w", err)
		}
		return resolvedUnitID, resolvedUnitName, nil
	}

	unit, err := txs.loadOrganizationUnit(ctx, queryer, organizationID, trimmedUnitID)
	if err != nil {
		return "", "", err
	}
	return unit.ID, unit.Name, nil
}

func (txs TxStore) loadOrganizationUnit(ctx context.Context, queryer queryRower, organizationID string, unitID string) (organizationUnitRecord, error) {
	row := queryer.QueryRow(ctx, `
SELECT ou.id,
       COALESCE(ou.parent_unit_id, ''),
       COALESCE(ou.manager_member_id, ''),
       ou.name,
       ou.slug,
       ou.status,
       ou.external_provider,
       ou.external_id,
       ou.synced_at,
       ou.sync_mode,
       ou.sync_source,
       COALESCE(osr.id, ''),
       COALESCE(osr.directory_revision, 0),
       COALESCE(osr.status, ''),
       ou.is_default
FROM organization_units ou
LEFT JOIN organization_directory_sync_runs osr
  ON osr.id = ou.last_sync_run_id
WHERE ou.organization_id = $1 AND ou.id = $2
`, organizationID, unitID)

	var record organizationUnitRecord
	var syncedAt sql.NullTime
	if err := row.Scan(
		&record.ID,
		&record.ParentUnitID,
		&record.ManagerMemberID,
		&record.Name,
		&record.Slug,
		&record.Status,
		&record.ExternalProvider,
		&record.ExternalID,
		&syncedAt,
		&record.SyncMode,
		&record.SyncSource,
		&record.LastSyncRunID,
		&record.LastSyncRevision,
		&record.LastSyncStatus,
		&record.IsDefault,
	); err != nil {
		if coredb.IsNotFound(err) {
			return organizationUnitRecord{}, ErrNotFound
		}
		return organizationUnitRecord{}, fmt.Errorf("load organization unit: %w", err)
	}
	if syncedAt.Valid {
		record.SyncedAt = &syncedAt.Time
	}
	return record, nil
}

func (txs TxStore) loadOrganizationUnitView(ctx context.Context, queryer queryer, organizationID string, unitID string) (OrganizationUnitView, error) {
	rows, err := queryer.Query(ctx, `
SELECT ou.id,
       COALESCE(ou.parent_unit_id, ''),
       COALESCE(ou.manager_member_id, ''),
       ou.name,
       ou.slug,
       ou.status,
       ou.external_provider,
       ou.external_id,
       ou.synced_at,
       ou.sync_mode,
       ou.sync_source,
       COALESCE(osr.id, ''),
       COALESCE(osr.directory_revision, 0),
       COALESCE(osr.status, ''),
       ou.is_default,
       SUM(CASE WHEN om.status = 'active' THEN 1 ELSE 0 END)
FROM organization_units ou
LEFT JOIN organization_directory_sync_runs osr
  ON osr.id = ou.last_sync_run_id
LEFT JOIN organization_member_unit_memberships oaum
  ON oaum.organization_unit_id = ou.id
 AND oaum.status = 'active'
LEFT JOIN organization_members om
  ON om.id = oaum.organization_member_id
WHERE ou.organization_id = $1
  AND ou.id = $2
GROUP BY ou.id, ou.parent_unit_id, ou.manager_member_id, ou.name, ou.slug, ou.status, ou.external_provider, ou.external_id, ou.synced_at, ou.sync_mode, ou.sync_source, osr.id, osr.directory_revision, osr.status, ou.is_default, ou.created_at
`, organizationID, unitID)
	if err != nil {
		return OrganizationUnitView{}, fmt.Errorf("load organization unit view: %w", err)
	}
	defer rows.Close()
	if !rows.Next() {
		if err := rows.Err(); err != nil {
			return OrganizationUnitView{}, fmt.Errorf("load organization unit view: %w", err)
		}
		return OrganizationUnitView{}, ErrNotFound
	}
	unit, err := scanOrganizationUnitView(rows)
	if err != nil {
		return OrganizationUnitView{}, err
	}
	if err := rows.Err(); err != nil {
		return OrganizationUnitView{}, fmt.Errorf("load organization unit view: %w", err)
	}
	return unit, nil
}

func (txs TxStore) loadOrganizationUnitMembership(ctx context.Context, queryer queryRower, organizationID string, unitID string, memberID string) (organizationMemberUnitMembershipRecord, error) {
	row := queryer.QueryRow(ctx, `
SELECT id, organization_member_id, organization_unit_id, source, status, is_primary
FROM organization_member_unit_memberships
WHERE organization_id = $1 AND organization_unit_id = $2 AND organization_member_id = $3
`, organizationID, unitID, memberID)

	var membership organizationMemberUnitMembershipRecord
	if err := row.Scan(&membership.ID, &membership.MemberID, &membership.UnitID, &membership.Source, &membership.Status, &membership.IsPrimary); err != nil {
		if coredb.IsNotFound(err) {
			return organizationMemberUnitMembershipRecord{}, ErrNotFound
		}
		return organizationMemberUnitMembershipRecord{}, fmt.Errorf("load organization unit membership: %w", err)
	}
	return membership, nil
}

func (txs TxStore) loadOrganizationUnitMemberView(ctx context.Context, queryer queryRower, organizationID string, unitID string, memberID string) (OrganizationUnitMemberView, error) {
	row := queryer.QueryRow(ctx, `
SELECT oaum.organization_unit_id,
       om.id,
       u.id,
       COALESCE(u.email, ''),
       u.name,
       u.display_name,
       oaum.source,
       oaum.status,
       oaum.is_primary,
       COALESCE(pu.id, ''),
       COALESCE(pu.name, '')
FROM organization_member_unit_memberships oaum
JOIN organization_members om ON om.id = oaum.organization_member_id
JOIN users u ON u.id = om.user_id
LEFT JOIN organization_member_unit_memberships primary_membership
  ON primary_membership.organization_id = oaum.organization_id
 AND primary_membership.organization_member_id = oaum.organization_member_id
 AND primary_membership.status = 'active'
 AND primary_membership.is_primary = TRUE
LEFT JOIN organization_units pu ON pu.id = primary_membership.organization_unit_id
WHERE oaum.organization_id = $1
  AND oaum.organization_unit_id = $2
  AND oaum.organization_member_id = $3
`, organizationID, unitID, memberID)

	var view OrganizationUnitMemberView
	if err := row.Scan(
		&view.UnitID,
		&view.MemberID,
		&view.UserID,
		&view.Email,
		&view.Name,
		&view.DisplayName,
		&view.Source,
		&view.Status,
		&view.IsPrimary,
		&view.PrimaryUnitID,
		&view.PrimaryUnitName,
	); err != nil {
		if coredb.IsNotFound(err) {
			return OrganizationUnitMemberView{}, ErrNotFound
		}
		return OrganizationUnitMemberView{}, fmt.Errorf("load organization unit member view: %w", err)
	}
	return view, nil
}

func (txs TxStore) upsertOrganizationUnitMembership(ctx context.Context, tx coredb.Tx, organizationID string, unitID string, accountID string, userID string, primary bool) error {
	now := time.Now().UTC()
	membershipID, err := identifiers.NewEntityID("oaum")
	if err != nil {
		return fmt.Errorf("generate organization unit membership id: %w", err)
	}
	if err := execPortableUpdateThenInsert(
		ctx,
		tx,
		`
SELECT 1
FROM organization_member_unit_memberships
WHERE organization_member_id = $1
  AND organization_unit_id = $2
`,
		[]any{accountID, unitID},
		`
UPDATE organization_member_unit_memberships
SET status = 'active',
    updated_at = $3
WHERE organization_member_id = $1
  AND organization_unit_id = $2
`,
		[]any{accountID, unitID, now},
		`
INSERT INTO organization_member_unit_memberships (
    id,
    organization_id,
    organization_unit_id,
    organization_member_id,
    source,
    status,
    is_primary,
    created_by_user_id,
    created_at,
    updated_at
) VALUES ($1, $2, $3, $4, 'direct', 'active', $5, $6, $7, $7)
`,
		[]any{membershipID, organizationID, unitID, accountID, primary, userID, now},
	); err != nil {
		return fmt.Errorf("upsert organization unit membership: %w", err)
	}
	return nil
}

func (txs TxStore) switchPrimaryUnitMembership(ctx context.Context, tx coredb.Tx, organizationID string, accountID string, unitID string) error {
	now := time.Now().UTC()
	if _, err := tx.Exec(ctx, `
UPDATE organization_member_unit_memberships
SET is_primary = FALSE, updated_at = $3
WHERE organization_id = $1 AND organization_member_id = $2 AND status = 'active'
`, organizationID, accountID, now); err != nil {
		return fmt.Errorf("clear primary unit membership: %w", err)
	}
	if _, err := tx.Exec(ctx, `
UPDATE organization_member_unit_memberships
SET is_primary = TRUE, status = 'active', updated_at = $4
WHERE organization_id = $1 AND organization_member_id = $2 AND organization_unit_id = $3
`, organizationID, accountID, unitID, now); err != nil {
		return fmt.Errorf("set primary unit membership: %w", err)
	}
	if _, err := tx.Exec(ctx, `
UPDATE organization_members
SET primary_unit_id = $3, updated_at = $4
WHERE organization_id = $1 AND id = $2
`, organizationID, accountID, unitID, now); err != nil {
		return fmt.Errorf("update organization member primary unit: %w", err)
	}
	return nil
}

func scanOrganizationUnitView(scanner interface {
	Scan(dest ...any) error
}) (OrganizationUnitView, error) {
	var unit OrganizationUnitView
	var syncedAt sql.NullTime
	if err := scanner.Scan(
		&unit.ID,
		&unit.ParentUnitID,
		&unit.ManagerMemberID,
		&unit.Name,
		&unit.Slug,
		&unit.Status,
		&unit.ExternalProvider,
		&unit.ExternalID,
		&syncedAt,
		&unit.SyncMode,
		&unit.SyncSource,
		&unit.LastSyncRunID,
		&unit.LastSyncDirectoryRevision,
		&unit.LastSyncRunStatus,
		&unit.IsDefault,
		&unit.MemberCount,
	); err != nil {
		return OrganizationUnitView{}, fmt.Errorf("scan organization unit: %w", err)
	}
	if syncedAt.Valid {
		unit.SyncedAt = &syncedAt.Time
	}
	unit.OrganizationSyncMetadata = organizationSyncMetadataView(
		unit.ExternalProvider,
		unit.ExternalID,
		unit.SyncedAt,
		unit.SyncMode,
		unit.SyncSource,
		unit.LastSyncRunID,
		unit.LastSyncDirectoryRevision,
		unit.LastSyncRunStatus,
	)
	return unit, nil
}

func requireLocallyMutableUnit(unit organizationUnitRecord) error {
	if unit.Status != organizationUnitStatusActive {
		return badRequest("unit is not active")
	}
	if strings.TrimSpace(unit.SyncMode) != "" && unit.SyncMode != organizationSyncModeLocal {
		return badRequest("unit is managed by provider sync")
	}
	return nil
}
