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/copy_draft/domain/entity"
	domrepo "haixun-backend/internal/model/copy_draft/domain/repository"
	domusecase "haixun-backend/internal/model/copy_draft/domain/usecase"

	"github.com/google/uuid"
)

type copyDraftUseCase struct {
	repo domrepo.Repository
}

func NewUseCase(repo domrepo.Repository) domusecase.UseCase {
	return &copyDraftUseCase{repo: repo}
}

func (u *copyDraftUseCase) Create(ctx context.Context, req domusecase.CreateRequest) (*domusecase.CopyDraftSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.PersonaID); err != nil {
		return nil, err
	}
	text := strings.TrimSpace(req.Text)
	if text == "" {
		return nil, app.For(code.Persona).InputMissingRequired("draft text is required")
	}
	draftType := strings.TrimSpace(req.DraftType)
	if draftType == "" {
		draftType = entity.DraftTypeViralReplica
	}
	item := &entity.CopyDraft{
		ID:             uuid.NewString(),
		TenantID:       req.TenantID,
		OwnerUID:       req.OwnerUID,
		PersonaID:      req.PersonaID,
		CopyMissionID:  strings.TrimSpace(req.CopyMissionID),
		ScanPostID:     strings.TrimSpace(req.ScanPostID),
		DraftType:      draftType,
		SortOrder:      req.SortOrder,
		Text:           text,
		Angle:          strings.TrimSpace(req.Angle),
		Hook:           strings.TrimSpace(req.Hook),
		Rationale:      strings.TrimSpace(req.Rationale),
		ReferenceNotes: strings.TrimSpace(req.ReferenceNotes),
		Sources:        req.Sources,
		Status:         "pending",
		CreateAt:       clock.NowUnixNano(),
	}
	if err := u.repo.Create(ctx, item); err != nil {
		return nil, err
	}
	summary := toSummary(*item)
	return &summary, nil
}

func (u *copyDraftUseCase) CreateMany(ctx context.Context, req domusecase.CreateManyRequest) ([]domusecase.CopyDraftSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.PersonaID); err != nil {
		return nil, err
	}
	if len(req.Drafts) == 0 {
		return nil, app.For(code.Persona).InputMissingRequired("drafts is required")
	}
	items := make([]*entity.CopyDraft, 0, len(req.Drafts))
	for idx, draftReq := range req.Drafts {
		text := strings.TrimSpace(draftReq.Text)
		if text == "" {
			continue
		}
		draftType := strings.TrimSpace(draftReq.DraftType)
		if draftType == "" {
			draftType = entity.DraftTypeMatrix
		}
		sortOrder := draftReq.SortOrder
		if sortOrder == 0 {
			sortOrder = idx + 1
		}
		items = append(items, &entity.CopyDraft{
			ID:             uuid.NewString(),
			TenantID:       req.TenantID,
			OwnerUID:       req.OwnerUID,
			PersonaID:      req.PersonaID,
			CopyMissionID:  strings.TrimSpace(draftReq.CopyMissionID),
			ScanPostID:     strings.TrimSpace(draftReq.ScanPostID),
			DraftType:      draftType,
			SortOrder:      sortOrder,
			Text:           text,
			Angle:          strings.TrimSpace(draftReq.Angle),
			Hook:           strings.TrimSpace(draftReq.Hook),
			Rationale:      strings.TrimSpace(draftReq.Rationale),
			ReferenceNotes: strings.TrimSpace(draftReq.ReferenceNotes),
			Sources:        draftReq.Sources,
			Status:         "pending",
			CreateAt:       clock.NowUnixNano(),
		})
	}
	if len(items) == 0 {
		return nil, app.For(code.Persona).InputMissingRequired("draft text is required")
	}
	if err := u.repo.CreateMany(ctx, items); err != nil {
		return nil, err
	}
	out := make([]domusecase.CopyDraftSummary, 0, len(items))
	for _, item := range items {
		out = append(out, toSummary(*item))
	}
	return out, nil
}

func (u *copyDraftUseCase) Get(ctx context.Context, tenantID, ownerUID, personaID, draftID string) (*domusecase.CopyDraftSummary, error) {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return nil, err
	}
	item, err := u.repo.Get(ctx, tenantID, ownerUID, personaID, draftID)
	if err != nil {
		return nil, err
	}
	summary := toSummary(*item)
	return &summary, nil
}

func (u *copyDraftUseCase) Update(ctx context.Context, req domusecase.UpdateRequest) (*domusecase.CopyDraftSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.PersonaID); err != nil {
		return nil, err
	}
	draftID := strings.TrimSpace(req.DraftID)
	if draftID == "" {
		return nil, app.For(code.Persona).InputMissingRequired("draft_id is required")
	}
	patch := map[string]interface{}{}
	if req.Patch.Text != nil {
		text := strings.TrimSpace(*req.Patch.Text)
		if text == "" {
			return nil, app.For(code.Persona).InputMissingRequired("draft text cannot be empty")
		}
		patch["text"] = text
	}
	if req.Patch.Hook != nil {
		patch["hook"] = strings.TrimSpace(*req.Patch.Hook)
	}
	if req.Patch.Angle != nil {
		patch["angle"] = strings.TrimSpace(*req.Patch.Angle)
	}
	if req.Patch.Status != nil {
		status := strings.TrimSpace(*req.Patch.Status)
		if status != "" && status != "pending" && status != "ready" {
			return nil, app.For(code.Persona).InputMissingRequired("status must be pending or ready")
		}
		if status != "" {
			patch["status"] = status
		}
	}
	if len(patch) == 0 {
		return nil, app.For(code.Persona).InputMissingRequired("no fields to update")
	}
	item, err := u.repo.Update(ctx, req.TenantID, req.OwnerUID, req.PersonaID, draftID, patch)
	if err != nil {
		return nil, err
	}
	summary := toSummary(*item)
	return &summary, nil
}

func (u *copyDraftUseCase) MarkPublished(ctx context.Context, req domusecase.MarkPublishedRequest) (*domusecase.CopyDraftSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.PersonaID); err != nil {
		return nil, err
	}
	draftID := strings.TrimSpace(req.DraftID)
	mediaID := strings.TrimSpace(req.MediaID)
	if draftID == "" || mediaID == "" {
		return nil, app.For(code.Persona).InputMissingRequired("draft_id and media_id are required")
	}
	now := clock.NowUnixNano()
	patch := map[string]interface{}{
		"status":              "published",
		"published_media_id":  mediaID,
		"published_permalink": strings.TrimSpace(req.Permalink),
		"published_at":        now,
	}
	item, err := u.repo.Update(ctx, req.TenantID, req.OwnerUID, req.PersonaID, draftID, patch)
	if err != nil {
		return nil, err
	}
	summary := toSummary(*item)
	return &summary, nil
}

func (u *copyDraftUseCase) ReplaceMissionMatrix(
	ctx context.Context,
	tenantID, ownerUID, personaID, missionID string,
	drafts []domusecase.CreateRequest,
) ([]domusecase.CopyDraftSummary, error) {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return nil, err
	}
	missionID = strings.TrimSpace(missionID)
	if missionID == "" {
		return nil, app.For(code.Persona).InputMissingRequired("copy_mission_id is required")
	}
	if err := u.repo.DeleteByMissionAndType(ctx, tenantID, ownerUID, personaID, missionID, entity.DraftTypeMatrix); err != nil {
		return nil, err
	}
	return u.CreateMany(ctx, domusecase.CreateManyRequest{
		TenantID:  tenantID,
		OwnerUID:  ownerUID,
		PersonaID: personaID,
		Drafts:    drafts,
	})
}

func (u *copyDraftUseCase) ClearByMission(ctx context.Context, tenantID, ownerUID, personaID, missionID string) error {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return err
	}
	missionID = strings.TrimSpace(missionID)
	if missionID == "" {
		return app.For(code.Persona).InputMissingRequired("copy_mission_id is required")
	}
	return u.repo.DeleteByMission(ctx, tenantID, ownerUID, personaID, missionID)
}

func (u *copyDraftUseCase) ListByMission(ctx context.Context, tenantID, ownerUID, personaID, missionID string, limit int) ([]domusecase.CopyDraftSummary, error) {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return nil, err
	}
	items, err := u.repo.ListByMission(ctx, tenantID, ownerUID, personaID, missionID, limit)
	if err != nil {
		return nil, err
	}
	out := make([]domusecase.CopyDraftSummary, 0, len(items))
	for _, item := range items {
		out = append(out, toSummary(item))
	}
	return out, nil
}

func (u *copyDraftUseCase) List(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]domusecase.CopyDraftSummary, error) {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return nil, err
	}
	items, err := u.repo.List(ctx, tenantID, ownerUID, personaID, limit)
	if err != nil {
		return nil, err
	}
	out := make([]domusecase.CopyDraftSummary, 0, len(items))
	for _, item := range items {
		out = append(out, toSummary(item))
	}
	return out, nil
}

func requireActor(tenantID, ownerUID, personaID string) error {
	if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
		return app.For(code.Auth).AuthUnauthorized("missing actor")
	}
	if strings.TrimSpace(personaID) == "" {
		return app.For(code.Persona).InputMissingRequired("persona_id is required")
	}
	return nil
}

func toSummary(item entity.CopyDraft) domusecase.CopyDraftSummary {
	return domusecase.CopyDraftSummary{
		ID:                 item.ID,
		PersonaID:          item.PersonaID,
		CopyMissionID:      item.CopyMissionID,
		ScanPostID:         item.ScanPostID,
		DraftType:          item.DraftType,
		SortOrder:          item.SortOrder,
		Text:               item.Text,
		Angle:              item.Angle,
		Hook:               item.Hook,
		Rationale:          item.Rationale,
		ReferenceNotes:     item.ReferenceNotes,
		Sources:            item.Sources,
		Status:             item.Status,
		PublishedMediaID:   item.PublishedMediaID,
		PublishedPermalink: item.PublishedPermalink,
		PublishedAt:        item.PublishedAt,
		CreateAt:           item.CreateAt,
	}
}
