package channel

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

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

const (
	ChannelAccessStatusJoined         = "joined"
	ChannelAccessStatusPublicJoinable = "public_joinable"
	ChannelAccessStatusInaccessible   = "inaccessible"
	ChannelAccessStatusNotFound       = "not_found"
)

// ChannelAccessResolution is the channel-route access decision for one
// workspace member and channel id. A joined result carries the full channel
// summary used by the conversation route. A public_joinable result carries only
// metadata safe to show before membership.
type ChannelAccessResolution struct {
	Status          string
	Channel         message.ChannelSummary
	JoinableChannel DiscoverableChannel
}

// ResolveChannelAccessForUser returns the single route-facing state for a
// channel id: joined, public-and-joinable, inaccessible, or not found.
func (store *Store) ResolveChannelAccessForUser(ctx context.Context, userID string, workspaceID string, channelID string) (ChannelAccessResolution, error) {
	trimmedChannelID := strings.TrimSpace(channelID)
	if trimmedChannelID == "" {
		return ChannelAccessResolution{}, badRequest("channelId is required")
	}

	memberID, err := store.requireWorkspaceAccess(ctx, userID, workspaceID)
	if err != nil {
		return ChannelAccessResolution{}, err
	}

	row := store.db.QueryRow(ctx, `
WITH active_scope_memberships AS (
    SELECT
        scope_id,
        member_id,
        SUM(CASE WHEN archived_at IS NULL THEN 1 ELSE 0 END) = 0 AS archived
    FROM scope_memberships
    WHERE member_id = $2
      AND visibility = 'public'
      AND removed_at IS NULL
      AND effective_to_seq IS NULL
    GROUP BY scope_id, member_id
)
SELECT
    s.id,
    COALESCE(NULLIF(p.slug, ''), s.id),
    COALESCE(NULLIF(p.display_name, ''), NULLIF(p.slug, ''), s.id),
    COALESCE(p.description, ''),
    CASE
      WHEN p.subsystem = 'tutorial' AND onboarding.status = 'completed' THEN 'im'
      ELSE COALESCE(NULLIF(p.subsystem, ''), 'im')
    END,
    p.subsystem = 'tutorial',
    COALESCE(NULLIF(p.visibility, ''), 'private'),
    COALESCE(wt.id, ''),
    COALESCE(wt.handle, ''),
    COALESCE(wt.display_name, ''),
    p.owner_member_id IS NOT NULL,
    COALESCE(p.owner_member_id = $2 AND wt.id IS NULL, FALSE),
    cms.pinned_at,
    s.latest_message_at,
    sm.member_id IS NOT NULL,
    COALESCE(sm.archived, FALSE)
FROM scopes s
JOIN scope_channel_profiles p
  ON p.scope_id = s.id
 AND p.workspace_id = s.workspace_id
LEFT JOIN active_scope_memberships sm
  ON sm.scope_id = s.id
 AND sm.member_id = $2
LEFT JOIN conversation_member_states cms
  ON cms.scope_id = s.id
 AND cms.member_id = $2
LEFT JOIN workspace_teams wt
  ON wt.workspace_id = s.workspace_id
 AND wt.channel_id = s.id
 AND wt.status = 'active'
LEFT JOIN desktop_onboarding_states onboarding
  ON onboarding.workspace_id = s.workspace_id
 AND onboarding.owner_member_id = $2
 AND onboarding.tutorial_channel_id = s.id
 AND onboarding.flow_version = 'v1'
WHERE s.workspace_id = $1
  AND s.id = $3
  AND s.type = 'channel'
  AND s.status = 'active'
  AND s.archived_at IS NULL
  AND p.subsystem IN ('im', 'team', 'tutorial')
  AND (p.subsystem <> 'tutorial' OR p.owner_member_id = $2)
LIMIT 1
`, workspaceID, memberID, trimmedChannelID)

	var (
		resolvedChannelID string
		slug              string
		displayName       string
		description       string
		subsystem         string
		tutorial          bool
		visibility        string
		teamID            string
		teamHandle        string
		teamDisplayName   string
		ownerPresent      bool
		canManage         bool
		pinnedAt          *time.Time
		lastMessageAt     *time.Time
		joined            bool
		archived          bool
	)
	if err := row.Scan(
		&resolvedChannelID,
		&slug,
		&displayName,
		&description,
		&subsystem,
		&tutorial,
		&visibility,
		&teamID,
		&teamHandle,
		&teamDisplayName,
		&ownerPresent,
		&canManage,
		&pinnedAt,
		&lastMessageAt,
		&joined,
		&archived,
	); err != nil {
		if coredb.IsNotFound(err) {
			return ChannelAccessResolution{Status: ChannelAccessStatusNotFound}, nil
		}
		return ChannelAccessResolution{}, fmt.Errorf("resolve channel access: %w", err)
	}

	teamManaged := strings.TrimSpace(teamID) != "" || strings.TrimSpace(subsystem) == message.ChannelSubsystemTeam
	if joined && (slug == "all" || ownerPresent || teamManaged) {
		return ChannelAccessResolution{
			Status: ChannelAccessStatusJoined,
			Channel: message.ChannelSummary{
				ChannelID:       resolvedChannelID,
				Slug:            slug,
				DisplayName:     displayName,
				Description:     description,
				Visibility:      visibility,
				Subsystem:       subsystem,
				Tutorial:        tutorial,
				TeamID:          teamID,
				TeamHandle:      teamHandle,
				TeamDisplayName: teamDisplayName,
				Joined:          true,
				CanInvite:       !teamManaged,
				CanManage:       canManage,
				Archived:        archived,
				PinnedAt:        pinnedAt,
				LastMessageAt:   lastMessageAt,
			},
		}, nil
	}

	if visibility == "public" && ownerPresent {
		return ChannelAccessResolution{
			Status: ChannelAccessStatusPublicJoinable,
			JoinableChannel: DiscoverableChannel{
				ChannelID:   resolvedChannelID,
				Slug:        slug,
				DisplayName: displayName,
				Description: description,
				Visibility:  visibility,
			},
		}, nil
	}

	return ChannelAccessResolution{Status: ChannelAccessStatusInaccessible}, nil
}
