package installation

import (
	"context"
	"fmt"
	"strings"

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

// AppManagementAttribution is the workspace-scoped market attribution of a
// workspace-created App: its creator and catalog visibility.
type AppManagementAttribution struct {
	CreatorMemberID   string
	CatalogVisibility string
}

// LoadAppManagementAttribution loads one App's workspace attribution row.
func LoadAppManagementAttribution(ctx context.Context, queryer coredb.QueryRower, workspaceID string, serviceDefinitionID string, forUpdate bool) (AppManagementAttribution, bool, error) {
	query := `
SELECT COALESCE(created_by_member_id, ''), catalog_visibility
FROM extension_catalog_attributions
WHERE workspace_id = $1
  AND service_definition_id = $2
LIMIT 1
`
	if forUpdate {
		query += "FOR UPDATE\n"
	}
	var attribution AppManagementAttribution
	if err := queryer.QueryRow(ctx, query, strings.TrimSpace(workspaceID), strings.TrimSpace(serviceDefinitionID)).Scan(&attribution.CreatorMemberID, &attribution.CatalogVisibility); err != nil {
		if coredb.IsNotFound(err) {
			return AppManagementAttribution{}, false, nil
		}
		return AppManagementAttribution{}, false, fmt.Errorf("load App management attribution: %w", err)
	}
	attribution.CreatorMemberID = strings.TrimSpace(attribution.CreatorMemberID)
	attribution.CatalogVisibility = strings.ToLower(strings.TrimSpace(attribution.CatalogVisibility))
	return attribution, true, nil
}

// HasActiveAppManager reports whether the member actively manages the App.
func HasActiveAppManager(ctx context.Context, queryer coredb.QueryRower, workspaceID string, appID string, memberID string, forUpdate bool) (bool, error) {
	query := `
SELECT id
FROM extension_definition_managers
WHERE workspace_id = $1
  AND service_definition_id = $2
  AND member_id = $3
  AND status = 'active'
  AND ended_at IS NULL
LIMIT 1
`
	if forUpdate {
		query += "FOR UPDATE\n"
	}
	var id string
	err := queryer.QueryRow(ctx, query, strings.TrimSpace(workspaceID), strings.TrimSpace(appID), strings.TrimSpace(memberID)).Scan(&id)
	if err == nil {
		return strings.TrimSpace(id) != "", nil
	}
	if coredb.IsNotFound(err) {
		return false, nil
	}
	return false, fmt.Errorf("load App manager: %w", err)
}

// DefinitionManagementAuthority is the batched workspace management
// authority for a set of App definitions: whether each is workspace-
// attributed and whether the given member actively manages it.
type DefinitionManagementAuthority struct {
	Attributed    bool
	ActiveManager bool
}

// LoadDefinitionManagementAuthorities resolves attribution and active
// management for every requested definition in one query.
func LoadDefinitionManagementAuthorities(ctx context.Context, queryer coredb.Queryer, workspaceID string, memberID string, definitionIDs []string) (map[string]DefinitionManagementAuthority, bool, error) {
	workspaceID = strings.TrimSpace(workspaceID)
	memberID = strings.TrimSpace(memberID)
	ids := UniqueTrimmedStrings(definitionIDs)
	if workspaceID == "" || len(ids) == 0 {
		return map[string]DefinitionManagementAuthority{}, false, nil
	}
	cte, cteArgs, _, err := coredb.BuildStringValuesCTE("requested_definitions", "definition_id", ids, 3)
	if err != nil {
		return nil, false, fmt.Errorf("build definitions CTE: %w", err)
	}
	args := append([]any{workspaceID, memberID}, cteArgs...)
	rows, err := queryer.Query(ctx, fmt.Sprintf(`
WITH %s
SELECT requested.definition_id,
       COALESCE(attribution.service_definition_id, '') AS attributed,
       COALESCE(manager.service_definition_id, '') AS active_manager
FROM requested_definitions requested
LEFT JOIN extension_catalog_attributions attribution
  ON attribution.workspace_id = $1
 AND attribution.service_definition_id = requested.definition_id
LEFT JOIN extension_definition_managers manager
  ON manager.workspace_id = $1
 AND manager.service_definition_id = requested.definition_id
 AND manager.member_id = $2
 AND manager.status = 'active'
 AND manager.ended_at IS NULL
ORDER BY requested.definition_id ASC
`, cte), args...)
	if err != nil {
		return nil, false, fmt.Errorf("load definition management authorities: %w", err)
	}
	defer rows.Close()
	authorities := make(map[string]DefinitionManagementAuthority, len(ids))
	for rows.Next() {
		var definitionID, attributed, activeManager string
		if err := rows.Scan(&definitionID, &attributed, &activeManager); err != nil {
			return nil, false, fmt.Errorf("scan definition management authority: %w", err)
		}
		authorities[strings.TrimSpace(definitionID)] = DefinitionManagementAuthority{
			Attributed:    strings.TrimSpace(attributed) != "",
			ActiveManager: strings.TrimSpace(activeManager) != "",
		}
	}
	if err := rows.Err(); err != nil {
		return nil, false, fmt.Errorf("iterate definition management authorities: %w", err)
	}
	return authorities, true, nil
}
