package notifications

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"switchboard-core/auth"
	"switchboard-core/events"
	"switchboard-core/models"
	"switchboard-core/store"
)

// ── Role Fallback ───────────────────────────
// Subscribes to the existing "role.fallback" EventBus event (emitted by
// capabilities/resolver.go since v0.17.0) and creates notifications for
// all admin users.

// roleFallbackPayload matches the payload emitted by the role resolver.
type roleFallbackPayload struct {
	Role     string `json:"role"`
	Primary  string `json:"primary_model"`
	Fallback string `json:"fallback_model"`
	Error    string `json:"error"`
}

// RoleFallbackHandler returns an events.Handler that creates admin
// notifications when a model role falls back.
func RoleFallbackHandler(svc *Service, stores store.Stores) events.Handler {
	return func(e events.Event) {
		var p roleFallbackPayload
		if err := json.Unmarshal(e.Payload, &p); err != nil {
			log.Printf("[notifications] failed to parse role.fallback payload: %v", err)
			return
		}

		title := fmt.Sprintf("Model fallback: %s → %s", p.Primary, p.Fallback)
		if p.Primary == "" {
			title = fmt.Sprintf("Role '%s' fell back to %s", p.Role, p.Fallback)
		}

		body := ""
		if p.Error != "" {
			body = fmt.Sprintf("Primary model error: %s", p.Error)
		}

		// Notify all Admins group members
		ctx := context.Background()
		admins, err := stores.Groups.ListMembers(ctx, auth.AdminsGroupID)
		if err != nil {
			log.Printf("[notifications] failed to list admins for role.fallback: %v", err)
			return
		}

		for _, u := range admins {
			n := models.Notification{
				UserID: u.UserID,
				Type:   models.NotifTypeRoleFallback,
				Title:  title,
				Body:   body,
			}
			if err := svc.Notify(ctx, &n); err != nil {
				log.Printf("[notifications] failed to create role.fallback notification for %s: %v", u.UserID, err)
			}
		}
	}
}

// ── Knowledge Base ──────────────────────────
// Called directly from the KB handler after indexing completes or fails.
// Not a bus subscriber — the KB handler calls these functions.

// NotifyKBReady creates a notification when a knowledge base finishes indexing.
func NotifyKBReady(svc *Service, userID, kbID, kbName string, chunkCount int) {
	if svc == nil {
		return
	}
	n := models.Notification{
		UserID:       userID,
		Type:         models.NotifTypeKBReady,
		Title:        fmt.Sprintf("Knowledge base \"%s\" ready (%d chunks)", kbName, chunkCount),
		ResourceType: models.ResourceTypeKnowledgeBase,
		ResourceID:   kbID,
	}
	if err := svc.Notify(context.Background(), &n); err != nil {
		log.Printf("[notifications] kb.ready failed for user %s: %v", userID, err)
	}
}

// NotifyKBError creates a notification when KB indexing fails.
func NotifyKBError(svc *Service, userID, kbID, kbName, errMsg string) {
	if svc == nil {
		return
	}
	n := models.Notification{
		UserID:       userID,
		Type:         models.NotifTypeKBError,
		Title:        fmt.Sprintf("Knowledge base \"%s\" indexing failed", kbName),
		Body:         errMsg,
		ResourceType: models.ResourceTypeKnowledgeBase,
		ResourceID:   kbID,
	}
	if err := svc.Notify(context.Background(), &n); err != nil {
		log.Printf("[notifications] kb.error failed for user %s: %v", userID, err)
	}
}

// ── Group Membership ────────────────────────
// Called directly from the group handler on add/remove.

// NotifyGroupMemberAdded creates a notification when a user is added to a group.
func NotifyGroupMemberAdded(svc *Service, userID, groupID, groupName string) {
	if svc == nil {
		return
	}
	n := models.Notification{
		UserID:       userID,
		Type:         models.NotifTypeGrantChanged,
		Title:        fmt.Sprintf("You were added to group \"%s\"", groupName),
		ResourceType: models.ResourceTypeGroup,
		ResourceID:   groupID,
	}
	if err := svc.Notify(context.Background(), &n); err != nil {
		log.Printf("[notifications] grant.changed (add) failed for user %s: %v", userID, err)
	}
}

// NotifyGroupMemberRemoved creates a notification when a user is removed from a group.
func NotifyGroupMemberRemoved(svc *Service, userID, groupID, groupName string) {
	if svc == nil {
		return
	}
	n := models.Notification{
		UserID:       userID,
		Type:         models.NotifTypeGrantChanged,
		Title:        fmt.Sprintf("You were removed from group \"%s\"", groupName),
		ResourceType: models.ResourceTypeGroup,
		ResourceID:   groupID,
	}
	if err := svc.Notify(context.Background(), &n); err != nil {
		log.Printf("[notifications] grant.changed (remove) failed for user %s: %v", userID, err)
	}
}

// ── User Mention ───────────────────────────
// Called from handlers/completion.go when an @mention targets a human user.

// NotifyUserMentioned creates a persisted notification for an @mention.
func NotifyUserMentioned(svc *Service, mentionedUserID, channelID, fromUserID, preview string) {
	if svc == nil {
		return
	}
	n := models.Notification{
		UserID:       mentionedUserID,
		Type:         models.NotifTypeUserMentioned,
		Title:        "You were mentioned in a conversation",
		Body:         preview,
		ResourceType: models.ResourceTypeChannel,
		ResourceID:   channelID,
	}
	if err := svc.Notify(context.Background(), &n); err != nil {
		log.Printf("[notifications] user.mentioned failed for user %s: %v", mentionedUserID, err)
	}
}

// ── Workflow Claimed ───────────────────────
// Called from handlers/workflow_assignments.go when a user claims an assignment.

// NotifyWorkflowClaimed creates a persisted notification when an assignment is claimed.
func NotifyWorkflowClaimed(svc *Service, claimerID, assignmentID, channelID string) {
	if svc == nil {
		return
	}
	n := models.Notification{
		UserID:       claimerID,
		Type:         models.NotifTypeWorkflowClaimed,
		Title:        "You claimed a workflow assignment",
		ResourceType: models.ResourceTypeChannel,
		ResourceID:   channelID,
	}
	if err := svc.Notify(context.Background(), &n); err != nil {
		log.Printf("[notifications] workflow.claimed failed for user %s: %v", claimerID, err)
	}
}
