package usecase

import (
	"context"
	"fmt"
	"strings"

	"apps/backend/internal/module/radar/domain"
)

// HealthGate checks AccountHealth before auto-send (outbox path).
// Level: ok | warn | throttle. Throttle must block automatic send.
type HealthGate interface {
	// WorstLevel returns the worst health level among usable accounts for owner.
	WorstLevel(ctx context.Context, ownerUID int64) (level string, advice string, err error)
}

// ReplyQueue puts a reply into the shared Outbox pipeline (same path as scout's
// outreach send). Implemented by an adapter over studio.Service.QueueExternalReply.
type ReplyQueue interface {
	QueueExternalReply(ctx context.Context, ownerUID int64, accountID, replyToMediaID, text, title string) (outboxID string, err error)
}

// MediaResolver turns a Threads permalink into a numeric Graph media ID.
// Opportunity.ExternalID/Permalink is a permalink, not a media id — implemented
// by an adapter that reuses scout's crawler session + resolver.
type MediaResolver interface {
	ResolveMediaID(ctx context.Context, ownerUID int64, permalink string) (string, error)
}

/*
MarkReplyUsed records that a draft was sent or copied (T550).

channel: outbox | manual_copy
  - dm variants: only manual_copy
  - outbox: rejects when health=throttle; warn still allowed (caller may surface advice).
    accountID selects which usable Threads account sends; required when ReplyQueue is
    configured (production). When ReplyQueue is nil (offline tests/demo) marking still
    succeeds without a real send, matching the previous behaviour.
*/
func (s *Service) MarkReplyUsed(ctx context.Context, ownerUID int64, opportunityID, replyID, channel, accountID string) (*domain.ReplyVariant, string, error) {
	channel = strings.TrimSpace(channel)
	if channel != domain.SentOutbox && channel != domain.SentManualCopy {
		return nil, "", fmt.Errorf("%w: channel must be outbox or manual_copy", domain.ErrValidation)
	}
	o, err := s.GetOpportunity(ctx, ownerUID, opportunityID)
	if err != nil {
		return nil, "", err
	}
	r, err := s.Repo.GetReply(ctx, replyID)
	if err != nil {
		return nil, "", err
	}
	if r.OwnerUID != ownerUID || r.OpportunityID != opportunityID {
		return nil, "", domain.ErrForbidden
	}
	if r.Variant == domain.ReplyDM && channel == domain.SentOutbox {
		return nil, "", fmt.Errorf("%w: dm reply cannot auto-send; use manual_copy", domain.ErrValidation)
	}

	var healthAdvice string
	if channel == domain.SentOutbox {
		if s.Health == nil {
			// No gate wired: still allow mark-used so offline demos work; production wires Health.
		} else {
			level, advice, herr := s.Health.WorstLevel(ctx, ownerUID)
			if herr != nil {
				return nil, "", herr
			}
			if level == "throttle" {
				return nil, advice, fmt.Errorf("%w: account health throttle blocks auto-send; copy and send manually", domain.ErrValidation)
			}
			if level == "warn" {
				healthAdvice = advice
				if healthAdvice == "" {
					healthAdvice = "帳號健康度偏黃，建議放慢自動送出。"
				}
			}
		}

		if s.ReplyQueue != nil {
			accountID = strings.TrimSpace(accountID)
			if accountID == "" {
				return nil, "", fmt.Errorf("%w: account_id required to send via outbox", domain.ErrValidation)
			}
			mediaID := o.ExternalID
			if s.MediaResolver != nil {
				resolved, rerr := s.MediaResolver.ResolveMediaID(ctx, ownerUID, o.Permalink)
				if rerr != nil {
					return nil, "", rerr
				}
				mediaID = resolved
			}
			title := "商機回覆"
			if o.AuthorHandle != "" {
				title += " · @" + o.AuthorHandle
			}
			outboxID, serr := s.ReplyQueue.QueueExternalReply(ctx, ownerUID, accountID, mediaID, r.Text, title)
			if serr != nil {
				return nil, "", serr
			}
			r.OutboxID = outboxID
		}
	}

	r.UsedAt = domain.NowNano()
	r.SentChannel = channel
	if err := s.Repo.SaveReply(ctx, r); err != nil {
		return nil, "", err
	}
	return r, healthAdvice, nil
}
