package models

import (
	"context"
	"fmt"
	"time"

	"github.com/bluesky-social/indigo/atproto/syntax"
	"tangled.org/core/idresolver"
)

type NotificationType string

const (
	NotificationTypeRepoStarred     NotificationType = "repo_starred"
	NotificationTypeIssueCreated    NotificationType = "issue_created"
	NotificationTypeIssueCommented  NotificationType = "issue_commented"
	NotificationTypePullCreated     NotificationType = "pull_created"
	NotificationTypePullCommented   NotificationType = "pull_commented"
	NotificationTypeFollowed        NotificationType = "followed"
	NotificationTypePullMerged      NotificationType = "pull_merged"
	NotificationTypeIssueClosed     NotificationType = "issue_closed"
	NotificationTypeIssueReopen     NotificationType = "issue_reopen"
	NotificationTypePullClosed      NotificationType = "pull_closed"
	NotificationTypePullReopen      NotificationType = "pull_reopen"
	NotificationTypeUserMentioned   NotificationType = "user_mentioned"
	NotificationTypeIssueAssigned   NotificationType = "issue_assigned"
	NotificationTypeIssueUnassigned NotificationType = "issue_unassigned"
	NotificationTypePullAssigned    NotificationType = "pull_assigned"
	NotificationTypePullUnassigned  NotificationType = "pull_unassigned"
)

var SocialNotificationTypes = []NotificationType{
	NotificationTypeRepoStarred,
	NotificationTypeFollowed,
}

var WorkNotificationTypes = []NotificationType{
	NotificationTypeIssueCreated,
	NotificationTypeIssueCommented,
	NotificationTypeIssueClosed,
	NotificationTypeIssueReopen,
	NotificationTypePullCreated,
	NotificationTypePullCommented,
	NotificationTypePullMerged,
	NotificationTypePullClosed,
	NotificationTypePullReopen,
	NotificationTypeUserMentioned,
	NotificationTypeIssueAssigned,
	NotificationTypeIssueUnassigned,
	NotificationTypePullAssigned,
	NotificationTypePullUnassigned,
}

type Notification struct {
	ID           int64
	RecipientDid string
	ActorDid     string
	Type         NotificationType
	EntityType   string
	EntityId     string
	Read         bool
	Created      time.Time

	// foreign key references
	RepoId  *int64
	IssueId *int64
	PullId  *int64
}

// lucide icon that represents this notification
func (n *Notification) Icon() string {
	switch n.Type {
	case NotificationTypeRepoStarred:
		return "star"
	case NotificationTypeIssueCreated:
		return "circle-dot"
	case NotificationTypeIssueCommented:
		return "message-square"
	case NotificationTypeIssueClosed:
		return "ban"
	case NotificationTypeIssueReopen:
		return "circle-dot"
	case NotificationTypePullCreated:
		return "git-pull-request-create"
	case NotificationTypePullCommented:
		return "message-square"
	case NotificationTypePullMerged:
		return "git-merge"
	case NotificationTypePullClosed:
		return "git-pull-request-closed"
	case NotificationTypePullReopen:
		return "git-pull-request-create"
	case NotificationTypeFollowed:
		return "user-plus"
	case NotificationTypeUserMentioned:
		return "at-sign"
	case NotificationTypeIssueAssigned, NotificationTypePullAssigned:
		return "user-round-arrow-forward"
	case NotificationTypeIssueUnassigned, NotificationTypePullUnassigned:
		return "user-round-minus"
	default:
		return ""
	}
}

type NotificationWithEntity struct {
	*Notification
	Repo  *Repo
	Issue *Issue
	Pull  *Pull
}

// URL returns the navigable path for the notification, resolving DIDs to
// handles via the provided resolver.
func (n *NotificationWithEntity) URL(res *idresolver.Resolver) string {
	resolve := func(did string) string {
		if id, err := res.ResolveIdent(context.Background(), did); err == nil && !id.Handle.IsInvalidHandle() {
			return id.Handle.String()
		}
		return did
	}

	switch n.Type {
	case NotificationTypeFollowed:
		return "/" + resolve(n.ActorDid)
	}
	if n.Repo == nil {
		return ""
	}
	repoHandle := resolve(n.Repo.Did)
	slug := n.Repo.Slug()
	switch {
	case n.Issue != nil:
		return fmt.Sprintf("/%s/%s/issues/%d", repoHandle, slug, n.Issue.IssueId)
	case n.Pull != nil:
		return fmt.Sprintf("/%s/%s/pulls/%d", repoHandle, slug, n.Pull.PullId)
	default:
		// repo_starred and other repo-level types
		return fmt.Sprintf("/%s/%s", repoHandle, slug)
	}
}

type NotificationPreferences struct {
	ID                 int64
	UserDid            syntax.DID
	RepoStarred        bool
	IssueCreated       bool
	IssueCommented     bool
	PullCreated        bool
	PullCommented      bool
	Followed           bool
	UserMentioned      bool
	PullMerged         bool
	IssueClosed        bool
	EmailNotifications bool
}

func (prefs *NotificationPreferences) ShouldNotify(t NotificationType) bool {
	switch t {
	case NotificationTypeRepoStarred:
		return prefs.RepoStarred
	case NotificationTypeIssueCreated:
		return prefs.IssueCreated
	case NotificationTypeIssueCommented:
		return prefs.IssueCommented
	case NotificationTypeIssueClosed:
		return prefs.IssueClosed
	case NotificationTypeIssueReopen:
		return prefs.IssueCreated // same pref for now
	case NotificationTypePullCreated:
		return prefs.PullCreated
	case NotificationTypePullCommented:
		return prefs.PullCommented
	case NotificationTypePullMerged:
		return prefs.PullMerged
	case NotificationTypePullClosed:
		return prefs.PullMerged // same pref for now
	case NotificationTypePullReopen:
		return prefs.PullCreated // same pref for now
	case NotificationTypeFollowed:
		return prefs.Followed
	case NotificationTypeUserMentioned,
		NotificationTypeIssueAssigned,
		NotificationTypeIssueUnassigned,
		NotificationTypePullAssigned,
		NotificationTypePullUnassigned:
		return prefs.UserMentioned
	default:
		return false
	}
}

func DefaultNotificationPreferences(user syntax.DID) *NotificationPreferences {
	return &NotificationPreferences{
		UserDid:            user,
		RepoStarred:        true,
		IssueCreated:       true,
		IssueCommented:     true,
		PullCreated:        true,
		PullCommented:      true,
		Followed:           true,
		UserMentioned:      true,
		PullMerged:         true,
		IssueClosed:        true,
		EmailNotifications: false,
	}
}
