package usecase

import (
	"context"
	"strings"

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

	"github.com/google/uuid"
)

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

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

func (u *inventoryUseCase) 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 *inventoryUseCase) 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.TargetDailyCount > 0 {
		item.TargetDailyCount = req.TargetDailyCount
	}
	if req.LowStockThreshold > 0 {
		item.LowStockThreshold = req.LowStockThreshold
	}
	if req.LookaheadDays > 0 {
		item.LookaheadDays = req.LookaheadDays
	}
	if strings.TrimSpace(req.Timezone) != "" {
		item.Timezone = strings.TrimSpace(req.Timezone)
	}
	item.Slots = slotsToEntity(req.Slots)
	item.SourceRefs = sourceRefsToEntity(req.SourceRefs)
	now := clock.NowUnixNano()
	item.UpdateAt = now
	saved, err := u.repo.Upsert(ctx, item)
	if err != nil {
		return nil, err
	}
	out := toSummary(saved)
	return &out, nil
}

func (u *inventoryUseCase) ListEnabled(ctx context.Context, limit int) ([]domusecase.PolicySummary, error) {
	items, err := u.repo.ListEnabled(ctx, limit)
	if err != nil {
		return nil, err
	}
	out := make([]domusecase.PolicySummary, 0, len(items))
	for _, item := range items {
		summary := toSummary(&item)
		out = append(out, summary)
	}
	return out, nil
}

func (u *inventoryUseCase) 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:           false,
		TargetDailyCount:  2,
		LowStockThreshold: 3,
		LookaheadDays:     7,
		Timezone:          "Asia/Taipei",
		Slots: []entity.Slot{
			{Weekday: 1, Time: "09:30"},
			{Weekday: 3, Time: "12:30"},
			{Weekday: 5, Time: "18:30"},
		},
		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, TargetDailyCount: item.TargetDailyCount,
		LowStockThreshold: item.LowStockThreshold, LookaheadDays: item.LookaheadDays,
		Timezone: item.Timezone, Slots: slotsFromEntity(item.Slots), SourceRefs: sourceRefsFromEntity(item.SourceRefs), UpdateAt: item.UpdateAt,
	}
}

func slotsToEntity(items []domusecase.Slot) []entity.Slot {
	out := make([]entity.Slot, 0, len(items))
	for _, item := range items {
		if strings.TrimSpace(item.Time) == "" {
			continue
		}
		out = append(out, entity.Slot{Weekday: item.Weekday, Time: strings.TrimSpace(item.Time)})
	}
	return out
}

func slotsFromEntity(items []entity.Slot) []domusecase.Slot {
	out := make([]domusecase.Slot, 0, len(items))
	for _, item := range items {
		out = append(out, domusecase.Slot{Weekday: item.Weekday, Time: item.Time})
	}
	return out
}

func sourceRefsToEntity(items []domusecase.SourceRef) []entity.SourceRef {
	out := make([]entity.SourceRef, 0, len(items))
	for _, item := range items {
		if strings.TrimSpace(item.Type) == "" {
			continue
		}
		out = append(out, entity.SourceRef{
			Type: strings.TrimSpace(item.Type), PersonaID: strings.TrimSpace(item.PersonaID),
			CopyMissionID: strings.TrimSpace(item.CopyMissionID), ScanPostID: strings.TrimSpace(item.ScanPostID), ManualSeed: strings.TrimSpace(item.ManualSeed),
		})
	}
	return out
}

func sourceRefsFromEntity(items []entity.SourceRef) []domusecase.SourceRef {
	out := make([]domusecase.SourceRef, 0, len(items))
	for _, item := range items {
		out = append(out, domusecase.SourceRef{
			Type: item.Type, PersonaID: item.PersonaID, CopyMissionID: item.CopyMissionID, ScanPostID: item.ScanPostID, ManualSeed: item.ManualSeed,
		})
	}
	return out
}
