package usecase

import (
	"context"
	"strings"
	"time"

	"haixun-backend/internal/library/clock"
	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	"haixun-backend/internal/model/publish_guard/domain/entity"
	domrepo "haixun-backend/internal/model/publish_guard/domain/repository"
	domusecase "haixun-backend/internal/model/publish_guard/domain/usecase"
	threadsaccountdomain "haixun-backend/internal/model/threads_account/domain/usecase"

	"github.com/google/uuid"
)

type guardUseCase struct {
	repo           domrepo.Repository
	threadsAccount threadsaccountdomain.UseCase
}

func NewUseCase(repo domrepo.Repository, threadsAccount threadsaccountdomain.UseCase) domusecase.UseCase {
	return &guardUseCase{repo: repo, threadsAccount: threadsAccount}
}

func (u *guardUseCase) Get(ctx context.Context, tenantID, ownerUID, accountID string) (*domusecase.PolicySummary, error) {
	if err := u.require(ctx, tenantID, ownerUID, accountID); err != nil {
		return nil, err
	}
	item, err := u.repo.Get(ctx, tenantID, ownerUID, accountID)
	if err != nil {
		if strings.Contains(err.Error(), "not found") {
			item = defaultPolicy(tenantID, ownerUID, accountID)
		} else {
			return nil, err
		}
	}
	out := toSummary(item)
	return &out, nil
}

func (u *guardUseCase) Upsert(ctx context.Context, req domusecase.UpsertRequest) (*domusecase.PolicySummary, error) {
	if err := u.require(ctx, req.TenantID, req.OwnerUID, req.AccountID); err != nil {
		return nil, err
	}
	item := defaultPolicy(req.TenantID, req.OwnerUID, req.AccountID)
	item.Enabled = req.Enabled
	if req.MaxDailyPosts > 0 {
		item.MaxDailyPosts = req.MaxDailyPosts
	}
	if req.MinIntervalMinutes > 0 {
		item.MinIntervalMinutes = req.MinIntervalMinutes
	}
	if req.ConsecutiveFailurePauseLimit > 0 {
		item.ConsecutiveFailurePauseLimit = req.ConsecutiveFailurePauseLimit
	}
	if req.Paused != nil {
		item.Paused = *req.Paused
	}
	item.PausedReason = strings.TrimSpace(req.PausedReason)
	item.UpdateAt = clock.NowUnixNano()
	saved, err := u.repo.Upsert(ctx, item)
	if err != nil {
		return nil, err
	}
	out := toSummary(saved)
	return &out, nil
}

func (u *guardUseCase) Resume(ctx context.Context, tenantID, ownerUID, accountID string) (*domusecase.PolicySummary, error) {
	policy, err := u.Get(ctx, tenantID, ownerUID, accountID)
	if err != nil {
		return nil, err
	}
	paused := false
	return u.Upsert(ctx, domusecase.UpsertRequest{
		TenantID: tenantID, OwnerUID: ownerUID, AccountID: accountID, Enabled: policy.Enabled,
		MaxDailyPosts: policy.MaxDailyPosts, MinIntervalMinutes: policy.MinIntervalMinutes,
		ConsecutiveFailurePauseLimit: policy.ConsecutiveFailurePauseLimit,
		Paused:                       &paused,
	})
}

func (u *guardUseCase) Pause(ctx context.Context, tenantID, ownerUID, accountID, reason string) (*domusecase.PolicySummary, error) {
	policy, err := u.Get(ctx, tenantID, ownerUID, accountID)
	if err != nil {
		return nil, err
	}
	paused := true
	return u.Upsert(ctx, domusecase.UpsertRequest{
		TenantID: tenantID, OwnerUID: ownerUID, AccountID: accountID, Enabled: policy.Enabled,
		MaxDailyPosts: policy.MaxDailyPosts, MinIntervalMinutes: policy.MinIntervalMinutes,
		ConsecutiveFailurePauseLimit: policy.ConsecutiveFailurePauseLimit,
		Paused:                       &paused, PausedReason: reason,
	})
}

func (u *guardUseCase) Check(ctx context.Context, req domusecase.CheckRequest) (*domusecase.CheckResult, error) {
	policy, err := u.Get(ctx, req.TenantID, req.OwnerUID, req.AccountID)
	if err != nil {
		return nil, err
	}
	if !policy.Enabled {
		return &domusecase.CheckResult{Allowed: true}, nil
	}
	now := req.Now
	if now <= 0 {
		now = clock.NowUnixNano()
	}
	if policy.Paused {
		return &domusecase.CheckResult{Allowed: false, Reason: firstNonEmpty(policy.PausedReason, "發文已暫停")}, nil
	}
	if policy.MaxDailyPosts > 0 && req.PublishedToday >= int64(policy.MaxDailyPosts) {
		return &domusecase.CheckResult{Allowed: false, DelayUntil: now + int64(time.Hour), Reason: "已達每日發文上限"}, nil
	}
	if policy.MinIntervalMinutes > 0 && req.LastPublishedAt > 0 {
		minNext := req.LastPublishedAt + int64(time.Duration(policy.MinIntervalMinutes)*time.Minute)
		if minNext > now {
			return &domusecase.CheckResult{Allowed: false, DelayUntil: minNext, Reason: "未達最小發文間隔"}, nil
		}
	}
	if policy.ConsecutiveFailurePauseLimit > 0 && req.FailureStreak >= policy.ConsecutiveFailurePauseLimit {
		return &domusecase.CheckResult{Allowed: false, Reason: "連續失敗達上限，已暫停帳號發文", ShouldPause: true}, nil
	}
	return &domusecase.CheckResult{Allowed: true}, nil
}

func (u *guardUseCase) require(ctx context.Context, tenantID, ownerUID, accountID string) error {
	if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
		return app.For(code.Auth).AuthUnauthorized("missing actor")
	}
	if strings.TrimSpace(accountID) == "" {
		return app.For(code.ThreadsAccount).InputMissingRequired("account_id is required")
	}
	if u.threadsAccount != nil {
		_, err := u.threadsAccount.Get(ctx, tenantID, ownerUID, accountID)
		return err
	}
	return nil
}

func defaultPolicy(tenantID, ownerUID, accountID string) *entity.Policy {
	now := clock.NowUnixNano()
	return &entity.Policy{
		ID: uuid.NewString(), TenantID: strings.TrimSpace(tenantID), OwnerUID: strings.TrimSpace(ownerUID), AccountID: strings.TrimSpace(accountID),
		Enabled: true, MaxDailyPosts: 6, MinIntervalMinutes: 90, ConsecutiveFailurePauseLimit: 3,
		CreateAt: now, UpdateAt: now,
	}
}

func toSummary(item *entity.Policy) domusecase.PolicySummary {
	if item == nil {
		return domusecase.PolicySummary{}
	}
	return domusecase.PolicySummary{
		AccountID: item.AccountID, Enabled: item.Enabled, MaxDailyPosts: item.MaxDailyPosts,
		MinIntervalMinutes: item.MinIntervalMinutes, ConsecutiveFailurePauseLimit: item.ConsecutiveFailurePauseLimit,
		Paused: item.Paused, PausedReason: item.PausedReason, UpdateAt: item.UpdateAt,
	}
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return strings.TrimSpace(value)
		}
	}
	return ""
}
