package auth

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"

	"chat-switchboard/config"
	"chat-switchboard/models"
	"chat-switchboard/store"
)

const sessionCookieName = "sb_session"

// CreateOrResumeSession looks up or creates a session participant for
// an anonymous visitor to a workflow channel.
//
// Two entry paths:
//   - Cookie-based (default): random token stored in sb_session cookie
//   - mTLS-based: cert fingerprint used as stable session identity
func CreateOrResumeSession(c *gin.Context, stores store.Stores, channelID string, cfg *config.Config) (*models.SessionParticipant, error) {
	// Check for existing session cookie
	token, _ := c.Cookie(sessionCookieName)

	// mTLS mode: use cert fingerprint as stable token
	if cfg.AuthMode == "mtls" {
		fp := c.GetHeader("X-SSL-Client-Fingerprint")
		if fp != "" {
			token = "mtls:" + fp
		}
	}

	// Resume existing session if token matches this channel
	if token != "" {
		session, err := stores.Sessions.GetByToken(c.Request.Context(), token)
		if err == nil && session.ChannelID == channelID {
			return session, nil
		}
		// Token exists but for different channel — fall through to create
		if err != nil && err != sql.ErrNoRows {
			log.Printf("[auth/session] warn: GetByToken error: %v", err)
		}
	}

	// Create new session
	token = "sess:" + uuid.New().String()
	displayName, err := generateVisitorName(c.Request.Context(), stores, channelID)
	if err != nil {
		displayName = "Visitor"
	}

	session := &models.SessionParticipant{
		SessionToken: token,
		ChannelID:    channelID,
		DisplayName:  displayName,
	}

	// mTLS mode: store fingerprint for team member visibility
	if cfg.AuthMode == "mtls" {
		fp := c.GetHeader("X-SSL-Client-Fingerprint")
		if fp != "" {
			session.Fingerprint = fp
		}
	}

	if err := stores.Sessions.Create(c.Request.Context(), session); err != nil {
		return nil, fmt.Errorf("create session: %w", err)
	}

	// Add as channel participant
	_ = stores.Channels.AddParticipant(c.Request.Context(), &models.ChannelParticipant{
		ChannelID:       channelID,
		ParticipantType: "session",
		ParticipantID:   session.ID,
		Role:            "visitor",
	})

	// Set cookie (httponly, secure, 30 day expiry)
	c.SetCookie(sessionCookieName, token, 60*60*24*30, "/", "", true, true)

	log.Printf("[auth/session] created session %s for channel %s (%s)", session.ID, channelID, displayName)
	return session, nil
}

// generateVisitorName produces "Visitor #N" based on existing session count.
func generateVisitorName(ctx context.Context, stores store.Stores, channelID string) (string, error) {
	count, err := stores.Sessions.CountForChannel(ctx, channelID)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("Visitor #%d", count+1), nil
}
