package main

import (
	"context"
	"errors"
	"fmt"
	jira "github.com/andygrunwald/go-jira/v2/cloud"
	"github.com/charmbracelet/huh/spinner"
)

var errorAborted = errors.New("aborted by user")

func CreateSpinner(title string) *spinner.Spinner {
	return spinner.New().
		Title(" " + title).
		Type(spinner.Globe)
}

func RunSpinnerRaw(title string, callback func(ctx context.Context) error) error {
	var err error
	finished := false
	ctx, cancel := context.WithCancel(context.Background())
	err2 := CreateSpinner(title).
		Action(func() {
			err = callback(ctx)
			finished = true
		}).Run()
	if err2 != nil {
		return err2
	}
	if !finished {
		cancel()
		return errorAborted
	}
	return err
}

func RunSpinner[T any](title string, callback func(ctx context.Context) (*T, any, error)) (*T, error) {
	var result *T
	err := RunSpinnerRaw(title, func(ctx context.Context) error {
		var err error
		result, _, err = callback(ctx)
		return err
	})
	return result, err
}

func GetAllStatuses(projectId string) (*jira.IssueTypesWithStatus, error) {
	return RunSpinner("Fetching statuses...", func(ctx context.Context) (*jira.IssueTypesWithStatus, any, error) {
		return jiraClient.Project.GetAllStatuses(ctx, projectId)
	})
}

func GetUserData() (*jira.User, error) {
	return RunSpinner("Fetching user info...", func(ctx context.Context) (*jira.User, any, error) {
		return jiraClient.User.GetCurrentUser(ctx)
	})
}

func GetAllProjects() (*jira.ProjectList, error) {
	return RunSpinner("Fetching all projects...", func(ctx context.Context) (*jira.ProjectList, any, error) {
		return jiraClient.Project.GetAll(ctx, nil)
	})
}

func GetAllBoardsForProject(projectIdOrKey string) (*jira.BoardsList, error) {
	return RunSpinner(fmt.Sprintf("Fetching boards for project '%s'...", projectIdOrKey), func(ctx context.Context) (*jira.BoardsList, any, error) {
		return jiraClient.Board.GetAllBoards(ctx, &jira.BoardListOptions{ProjectKeyOrID: projectIdOrKey})
	})
}

func GetBoardConfiguration(boardId int) (*jira.BoardConfiguration, error) {
	return RunSpinner("Fetching board...", func(ctx context.Context) (*jira.BoardConfiguration, any, error) {
		return jiraClient.Board.GetBoardConfiguration(ctx, boardId)
	})
}

func GetCurrentSprint(boardId int) (*jira.Sprint, error) {
	list, err := RunSpinner("Fetching sprint...", func(ctx context.Context) (*jira.SprintsList, any, error) {
		return jiraClient.Board.GetAllSprints(ctx, int64(boardId), &jira.GetAllSprintsOptions{
			State: "active",
		})
	})
	if err != nil {
		return nil, err
	}
	if len(list.Values) == 0 {
		return nil, errors.New("No active sprint found")
	}
	if len(list.Values) > 1 {
		return nil, errors.New("Multiple active sprints found")
	}
	return &list.Values[0], nil
}

func GetIssuesForSprint(sprintId int) ([]jira.Issue, error) {
	issues, err := RunSpinner("Fetching issues...", func(ctx context.Context) (*[]jira.Issue, any, error) {
		list, t, err := jiraClient.Sprint.GetIssuesForSprint(ctx, sprintId)
		return &list, t, err
	})
	if issues == nil {
		return nil, err
	} else {
		return *issues, err
	}
}

func GetIssue(issueKey string) (*jira.Issue, error) {
	return RunSpinner("Fetching issue...", func(ctx context.Context) (*jira.Issue, any, error) {
		return jiraClient.Issue.Get(ctx, issueKey, nil)
	})
}

func UpdateIssueTitle(issue string, title string) error {
	return RunSpinnerRaw("Updating issue title...", func(ctx context.Context) error {
		resp, err := jiraClient.Issue.UpdateIssue(ctx, issue, map[string]any{
			"fields": map[string]any{
				"summary": title,
			},
		})
		if err != nil {
			return err
		}
		return resp.Body.Close()
	})
}