// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT

package forgejo

import (
	"context"
	"fmt"

	"code.forgejo.org/f3/gof3/v3/f3"
	"code.forgejo.org/f3/gof3/v3/id"
	f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
	"code.forgejo.org/f3/gof3/v3/tree/generic"

	forgejo_sdk "code.forgejo.org/f3/gof3/v3/forges/forgejo/sdk"
)

type reaction struct {
	common

	forgejoReaction *forgejo_sdk.Reaction
	exists          bool
	id              string
}

var _ f3_tree.ForgeDriverInterface = &reaction{}

func newReaction() generic.NodeDriverInterface {
	return &reaction{}
}

func (o *reaction) SetNative(reaction any) {
	o.forgejoReaction = reaction.(*forgejo_sdk.Reaction)
	o.exists = true
	o.id = fmt.Sprintf("%d %s", o.forgejoReaction.User.ID, o.forgejoReaction.Reaction)
}

func (o *reaction) GetNativeID() string {
	return o.id
}

func (o *reaction) NewFormat() f3.Interface {
	node := o.GetNode()
	return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
}

func (o *reaction) ToFormat() f3.Interface {
	if o.forgejoReaction == nil {
		return o.NewFormat()
	}

	reaction := &f3.Reaction{
		Common:  f3.NewCommon(o.GetNativeID()),
		Content: o.forgejoReaction.Reaction,
	}

	if o.forgejoReaction.User.ID != 0 {
		reaction.UserID = f3_tree.NewUserReference(o.forgejoReaction.User.ID)
	}

	return reaction
}

func (o *reaction) FromFormat(content f3.Interface) {
	reaction := content.(*f3.Reaction)
	o.forgejoReaction = &forgejo_sdk.Reaction{
		Reaction: reaction.Content,
	}
	if reaction.UserID != nil {
		o.forgejoReaction.User = &forgejo_sdk.User{
			ID: reaction.UserID.GetIDAsInt(),
		}
	}

	o.id = reaction.GetID()
}

func (o *reaction) Get(ctx context.Context) bool {
	return o.exists
}

func (o *reaction) Put(ctx context.Context) id.NodeID {
	node := o.GetNode()
	o.Trace("%s", node.GetID())

	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	reactionable := f3_tree.GetReactionable(o.GetNode())

	if o.forgejoReaction.User != nil {
		o.maybeSudoID(ctx, o.forgejoReaction.User.ID)
		defer o.notSudo()
	}

	var err error
	var reaction *forgejo_sdk.Reaction

	switch reactionable.GetKind() {
	case f3_tree.KindIssue, f3_tree.KindPullRequest:
		commentable := f3_tree.GetCommentableID(o.GetNode())
		reaction, _, err = o.getClient().PostIssueReaction(owner, project, commentable, o.forgejoReaction.Reaction)
	case f3_tree.KindComment:
		comment := f3_tree.GetCommentID(o.GetNode())
		reaction, _, err = o.getClient().PostIssueCommentReaction(owner, project, comment, o.forgejoReaction.Reaction)
	default:
		panic(fmt.Errorf("unexpected type %T", owner))
	}

	if err != nil {
		panic(err)
	}
	o.SetNative(reaction)
	return id.NewNodeID(o.GetNativeID())
}

func (o *reaction) Delete(ctx context.Context) {
	node := o.GetNode()
	o.Trace("%s", node.GetID())

	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	reactionable := f3_tree.GetReactionable(o.GetNode())
	reactionableID := f3_tree.GetReactionableID(o.GetNode())

	var err error

	switch reactionable.GetKind() {
	case f3_tree.KindIssue, f3_tree.KindPullRequest:
		_, err = o.getClient().DeleteIssueReaction(owner, project, reactionableID, o.forgejoReaction.Reaction)
	case f3_tree.KindComment:
		_, err = o.getClient().DeleteIssueCommentReaction(owner, project, reactionableID, o.forgejoReaction.Reaction)
	default:
		panic(fmt.Errorf("unexpected type %T", owner))
	}
	if err != nil {
		panic(err)
	}
	o.exists = false
}
