package email

import (
	"context"
	"fmt"
	"log"

	securejoin "github.com/cyphar/filepath-securejoin"
	"tangled.sh/tangled.sh/core/appview/config"
	"tangled.sh/tangled.sh/core/appview/db"
	"tangled.sh/tangled.sh/core/appview/notify"
	"tangled.sh/tangled.sh/core/idresolver"
)

type EmailNotifier struct {
	db         *db.DB
	idResolver *idresolver.Resolver
	Config     *config.Config
	notify.BaseNotifier
}

func NewEmailNotifier(
	db *db.DB,
	idResolver *idresolver.Resolver,
	config *config.Config,
) notify.Notifier {
	return &EmailNotifier{
		db,
		idResolver,
		config,
		notify.BaseNotifier{},
	}
}

var _ notify.Notifier = &EmailNotifier{}

// TODO: yeah this is just bad design. should be moved under idResolver ore include repoResolver at first place
func (n *EmailNotifier) repoOwnerSlashName(ctx context.Context, repo *db.Repo) (string, error) {
	repoOwnerID, err := n.idResolver.ResolveIdent(ctx, repo.Did)
	if err != nil || repoOwnerID.Handle.IsInvalidHandle() {
		return "", fmt.Errorf("resolve comment owner did: %w", err)
	}
	repoOwnerHandle := repoOwnerID.Handle
	var repoOwnerSlashName string
	if repoOwnerHandle != "" && !repoOwnerHandle.IsInvalidHandle() {
		repoOwnerSlashName, _ = securejoin.SecureJoin(fmt.Sprintf("@%s", repoOwnerHandle), repo.Name)
	} else {
		repoOwnerSlashName = repo.DidSlashRepo()
	}
	return repoOwnerSlashName, nil
}

func (n *EmailNotifier) buildIssueEmail(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment) (Email, error) {
	commentOwner, err := n.idResolver.ResolveIdent(ctx, comment.OwnerDid)
	if err != nil || commentOwner.Handle.IsInvalidHandle() {
		return Email{}, fmt.Errorf("resolve comment owner did: %w", err)
	}
	baseUrl := n.Config.Core.AppviewHost
	repoOwnerSlashName, err := n.repoOwnerSlashName(ctx, repo)
	if err != nil {
		return Email{}, nil
	}
	url := fmt.Sprintf("%s/%s/issues/%d#comment-%d", baseUrl, repoOwnerSlashName, comment.Issue, comment.CommentId)
	return Email{
		APIKey:  n.Config.Resend.ApiKey,
		From:    n.Config.Resend.SentFrom,
		Subject: fmt.Sprintf("[%s] %s (issue#%d)", repoOwnerSlashName, issue.Title, issue.IssueId),
		Html:    fmt.Sprintf(`<p><b>@%s</b> mentioned you</p><a href="%s">View it on tangled.sh</a>.`, commentOwner.Handle.String(), url),
	}, nil
}

func (n *EmailNotifier) buildPullEmail(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment) (Email, error) {
	commentOwner, err := n.idResolver.ResolveIdent(ctx, comment.OwnerDid)
	if err != nil || commentOwner.Handle.IsInvalidHandle() {
		return Email{}, fmt.Errorf("resolve comment owner did: %w", err)
	}
	repoOwnerSlashName, err := n.repoOwnerSlashName(ctx, repo)
	if err != nil {
		return Email{}, nil
	}
	baseUrl := n.Config.Core.AppviewHost
	url := fmt.Sprintf("%s/%s/pulls/%d#comment-%d", baseUrl, repoOwnerSlashName, comment.PullId, comment.ID)
	return Email{
		APIKey:  n.Config.Resend.ApiKey,
		From:    n.Config.Resend.SentFrom,
		Subject: fmt.Sprintf("[%s] %s (pr#%d)", repoOwnerSlashName, pull.Title, pull.PullId),
		Html:    fmt.Sprintf(`<p><b>@%s</b> mentioned you</p><a href="%s">View it on tangled.sh</a>.`, commentOwner.Handle.String(), url),
	}, nil
}

func (n *EmailNotifier) gatherRecipientEmails(ctx context.Context, handles []string) []string {
	recipients := []string{}
	resolvedIdents := n.idResolver.ResolveIdents(ctx, handles)
	for _, id := range resolvedIdents {
		email, err := db.GetPrimaryEmail(n.db, id.DID.String())
		if err != nil {
			log.Println("failed to get primary email:", err)
			continue
		}
		recipients = append(recipients, email.Address)
	}
	return recipients
}

func (n *EmailNotifier) NewIssueComment(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment, mentions []string) {
	email, err := n.buildIssueEmail(ctx, repo, issue, comment)
	if err != nil {
		log.Println("failed to create issue-email:", err)
		return
	}
	// TODO: get issue-subscribed user DIDs and merge with mentioned users
	recipients := n.gatherRecipientEmails(ctx, mentions)
	log.Println("sending email to:", recipients)
	if err = SendEmail(email, recipients...); err != nil {
		log.Println("error sending email:", err)
	}
}

func (n *EmailNotifier) NewPullComment(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, mentions []string) {
	email, err := n.buildPullEmail(ctx, repo, pull, comment)
	if err != nil {
		log.Println("failed to create pull-email:", err)
	}
	recipients := n.gatherRecipientEmails(ctx, mentions)
	log.Println("sending email to:", recipients)
	if err = SendEmail(email); err != nil {
		log.Println("error sending email:", err)
	}
}
