// 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"
	"code.forgejo.org/f3/gof3/v3/util"

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

type reviewComment struct {
	common

	forgejoReviewComment *forgejo_sdk.PullReviewComment
}

var _ f3_tree.ForgeDriverInterface = &reviewComment{}

func newReviewComment() generic.NodeDriverInterface {
	return &reviewComment{}
}

func (o *reviewComment) SetNative(reviewComment any) {
	o.forgejoReviewComment = reviewComment.(*forgejo_sdk.PullReviewComment)
}

func (o *reviewComment) GetNativeID() string {
	return fmt.Sprintf("%d", o.forgejoReviewComment.ID)
}

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

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

	line := int(o.forgejoReviewComment.LineNum)
	if o.forgejoReviewComment.OldLineNum > 0 {
		line = int(o.forgejoReviewComment.OldLineNum) * -1
	}

	return &f3.ReviewComment{
		Common:    f3.NewCommon(o.GetNativeID()),
		PosterID:  f3_tree.NewUserReference(o.forgejoReviewComment.Reviewer.ID),
		Content:   o.forgejoReviewComment.Body,
		TreePath:  o.forgejoReviewComment.Path,
		DiffHunk:  o.forgejoReviewComment.DiffHunk,
		Line:      line,
		CommitID:  o.forgejoReviewComment.CommitID,
		CreatedAt: o.forgejoReviewComment.Created,
		UpdatedAt: o.forgejoReviewComment.Updated,
	}
}

func (o *reviewComment) FromFormat(content f3.Interface) {
	reviewComment := content.(*f3.ReviewComment)
	o.forgejoReviewComment = &forgejo_sdk.PullReviewComment{
		ID: util.ParseInt(reviewComment.GetID()),
		Reviewer: &forgejo_sdk.User{
			ID: reviewComment.PosterID.GetIDAsInt(),
		},
		Path:     reviewComment.TreePath,
		Body:     reviewComment.Content,
		DiffHunk: reviewComment.DiffHunk,
		CommitID: reviewComment.CommitID,
		Created:  reviewComment.CreatedAt,
		Updated:  reviewComment.UpdatedAt,
	}

	if reviewComment.Line > 0 {
		o.forgejoReviewComment.LineNum = uint64(reviewComment.Line)
	} else {
		o.forgejoReviewComment.OldLineNum = uint64(reviewComment.Line * -1)
	}
}

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

	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	pullRequest := f3_tree.GetPullRequestID(o.GetNode())
	review := f3_tree.GetReviewID(o.GetNode())
	comment := f3_tree.GetReviewCommentID(o.GetNode())

	reviewComment, resp, err := o.getClient().GetPullReviewComment(owner, project, pullRequest, review, comment)
	if resp.StatusCode == 404 {
		return false
	}
	if err != nil {
		panic(fmt.Errorf("reviewComment %v %w", o, err))
	}
	o.forgejoReviewComment = reviewComment
	return true
}

func (o *reviewComment) 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())
	pullRequest := f3_tree.GetPullRequestID(o.GetNode())
	review := f3_tree.GetReviewID(o.GetNode())

	o.maybeSudoID(ctx, o.forgejoReviewComment.Reviewer.ID)
	defer o.notSudo()

	reviewComment, _, err := o.getClient().CreatePullReviewComment(owner, project, pullRequest, review, forgejo_sdk.CreatePullReviewComment{
		Path:       o.forgejoReviewComment.Path,
		Body:       o.forgejoReviewComment.Body,
		LineNum:    o.forgejoReviewComment.LineNum,
		OldLineNum: o.forgejoReviewComment.OldLineNum,
	})
	if err != nil {
		panic(err)
	}
	o.forgejoReviewComment = reviewComment
	return id.NewNodeID(o.GetNativeID())
}

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

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

	_, _, err := o.getClient().EditIssueComment(owner, project, node.GetID().Int64(), forgejo_sdk.EditIssueCommentOption{
		Body: o.forgejoReviewComment.Body,
	})
	if err != nil {
		panic(err)
	}
}

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

	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	pullRequest := f3_tree.GetPullRequestID(o.GetNode())
	review := f3_tree.GetReviewID(o.GetNode())
	comment := f3_tree.GetReviewCommentID(o.GetNode())

	resp, err := o.getClient().DeletePullReviewComment(owner, project, pullRequest, review, comment)
	if resp.StatusCode != 204 {
		panic(fmt.Errorf("unexpected status code deleting %d %d %v", comment, resp.StatusCode, resp))
	}
	if err != nil {
		panic(err)
	}
}
