package app

import (
	"context"
	"fmt"
	"sort"
	"strings"
)

// ListAppInstallationChannelsForUser returns the active channels that both the
// caller and the selected installation can currently see. The query is
// installation-bound and does not hydrate channel member lists one by one.
func (store *Store) ListAppInstallationChannelsForUser(ctx context.Context, userID, workspaceID, installationID string) ([]AppInstallationChannelView, error) {
	workspaceID = strings.TrimSpace(workspaceID)
	installationID = strings.TrimSpace(installationID)
	installations, err := store.ListAppInstallationsForUser(ctx, userID, workspaceID)
	if err != nil {
		return nil, err
	}
	var selected *AppInstallationView
	for idx := range installations {
		if strings.TrimSpace(installations[idx].ID) == installationID {
			selected = &installations[idx]
			break
		}
	}
	if selected == nil || strings.TrimSpace(selected.MemberID) == "" {
		return nil, ErrNotFound
	}
	actor, err := store.requireWorkspaceAccess(ctx, userID, workspaceID)
	if err != nil {
		return nil, err
	}

	rows, err := store.db.Query(ctx, `
SELECT channel.id,
       profile.slug,
       COALESCE(NULLIF(profile.display_name, ''), profile.slug),
       COALESCE(profile.description, ''),
       COALESCE(NULLIF(profile.visibility, ''), 'private'),
       COALESCE((
         profile.owner_member_id = $4
         OR app_membership.owner_member_id = $4
         OR EXISTS (
           SELECT 1
           FROM service_target_bindings binding
           JOIN service_members service_member
             ON service_member.id = binding.service_member_id
            AND service_member.workspace_id = binding.workspace_id
            AND service_member.member_id = $3
            AND service_member.status = 'active'
           WHERE binding.workspace_id = $1
             AND binding.service_installation_id = $2
             AND binding.target_kind = 'channel'
             AND binding.target_ref = channel.id
             AND binding.status = 'active'
             AND binding.removed_at IS NULL
             AND binding.created_by_member_id = $4
         )
       ), FALSE) AS owner_or_adder
FROM scopes channel
JOIN scope_channel_profiles profile
  ON profile.scope_id = channel.id
 AND profile.workspace_id = channel.workspace_id
JOIN scope_memberships app_membership
  ON app_membership.scope_id = channel.id
 AND app_membership.member_id = $3
 AND app_membership.visibility = 'public'
 AND app_membership.removed_at IS NULL
 AND app_membership.archived_at IS NULL
 AND app_membership.effective_to_seq IS NULL
WHERE channel.workspace_id = $1
  AND channel.type = 'channel'
  AND channel.status = 'active'
  AND channel.archived_at IS NULL
  AND profile.slug <> 'all'
  AND EXISTS (
    SELECT 1
    FROM scope_memberships viewer_membership
    WHERE viewer_membership.scope_id = channel.id
      AND viewer_membership.member_id = $4
      AND viewer_membership.visibility = 'public'
      AND viewer_membership.removed_at IS NULL
      AND viewer_membership.effective_to_seq IS NULL
  )
ORDER BY COALESCE(NULLIF(profile.display_name, ''), profile.slug), channel.id
`, workspaceID, installationID, strings.TrimSpace(selected.MemberID), strings.TrimSpace(actor.Member.ID))
	if err != nil {
		return nil, fmt.Errorf("list App installation channels: %w", err)
	}
	defer rows.Close()

	channels := make([]AppInstallationChannelView, 0)
	for rows.Next() {
		var channel AppInstallationChannelView
		var ownerOrAdder bool
		if err := rows.Scan(&channel.ID, &channel.Slug, &channel.DisplayName, &channel.Description, &channel.Visibility, &ownerOrAdder); err != nil {
			return nil, fmt.Errorf("scan App installation channel: %w", err)
		}
		channel.CanRemove = selected.CanManageApp ||
			(selected.CatalogVisibility == "public" && ownerOrAdder)
		channels = append(channels, channel)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate App installation channels: %w", err)
	}
	sort.SliceStable(channels, func(left, right int) bool {
		return channels[left].DisplayName < channels[right].DisplayName
	})
	return channels, nil
}
