// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package note

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"strings"

	"git.secluded.site/go-lunatask"
	"git.secluded.site/lune/internal/client"
	"git.secluded.site/lune/internal/completion"
	"git.secluded.site/lune/internal/config"
	"git.secluded.site/lune/internal/ui"
	"github.com/spf13/cobra"
)

// ErrUnknownNotebook indicates the specified notebook key was not found in config.
var ErrUnknownNotebook = errors.New("unknown notebook key")

// ErrNoInput indicates no input was provided on stdin.
var ErrNoInput = errors.New("no input provided on stdin")

// AddCmd creates a new note. Exported for potential use by shortcuts.
var AddCmd = &cobra.Command{
	Use:   "add [NAME]",
	Short: "Create a new note",
	Long: `Create a new note in Lunatask.

The note name is optional. Use flags to set additional properties.
Use "-" as content flag value to read from stdin.`,
	RunE: runAdd,
}

func init() {
	AddCmd.Flags().StringP("notebook", "b", "", "Notebook key (from config)")
	AddCmd.Flags().StringP("content", "c", "", "Note content (use - for stdin)")
	AddCmd.Flags().String("source", "", "Source identifier")
	AddCmd.Flags().String("source-id", "", "Source ID")

	_ = AddCmd.RegisterFlagCompletionFunc("notebook", completion.Notebooks)
}

func runAdd(cmd *cobra.Command, args []string) error {
	apiClient, err := client.New()
	if err != nil {
		return err
	}

	builder := apiClient.NewNote()

	if len(args) > 0 {
		name, err := resolveName(args[0])
		if err != nil {
			return err
		}

		builder.WithName(name)
	}

	if err := applyNotebook(cmd, builder); err != nil {
		return err
	}

	if err := applyContent(cmd, builder); err != nil {
		return err
	}

	applySource(cmd, builder)

	note, err := ui.Spin("Creating note…", func() (*lunatask.Note, error) {
		return builder.Create(cmd.Context())
	})
	if err != nil {
		return err
	}

	if note == nil {
		fmt.Fprintln(cmd.OutOrStdout(), ui.Warning.Render("Note already exists (duplicate source)"))

		return nil
	}

	fmt.Fprintln(cmd.OutOrStdout(), ui.Success.Render("Created note: "+note.ID))

	return nil
}

func resolveName(arg string) (string, error) {
	if arg != "-" {
		return arg, nil
	}

	scanner := bufio.NewScanner(os.Stdin)
	if scanner.Scan() {
		return strings.TrimSpace(scanner.Text()), nil
	}

	if err := scanner.Err(); err != nil {
		return "", fmt.Errorf("reading stdin: %w", err)
	}

	return "", ErrNoInput
}

func applyNotebook(cmd *cobra.Command, builder *lunatask.NoteBuilder) error {
	notebookKey, _ := cmd.Flags().GetString("notebook")
	if notebookKey == "" {
		cfg, err := config.Load()
		if err != nil && !errors.Is(err, config.ErrNotFound) {
			return err
		}

		if cfg != nil {
			notebookKey = cfg.Defaults.Notebook
		}
	}

	if notebookKey == "" {
		return nil
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	notebook := cfg.NotebookByKey(notebookKey)
	if notebook == nil {
		return fmt.Errorf("%w: %s", ErrUnknownNotebook, notebookKey)
	}

	builder.InNotebook(notebook.ID)

	return nil
}

func applyContent(cmd *cobra.Command, builder *lunatask.NoteBuilder) error {
	content, _ := cmd.Flags().GetString("content")
	if content == "" {
		return nil
	}

	resolved, err := resolveContent(content)
	if err != nil {
		return err
	}

	builder.WithContent(resolved)

	return nil
}

func resolveContent(content string) (string, error) {
	if content != "-" {
		return content, nil
	}

	data, err := os.ReadFile("/dev/stdin")
	if err != nil {
		return "", fmt.Errorf("reading stdin: %w", err)
	}

	return strings.TrimSpace(string(data)), nil
}

func applySource(cmd *cobra.Command, builder *lunatask.NoteBuilder) {
	source, _ := cmd.Flags().GetString("source")
	sourceID, _ := cmd.Flags().GetString("source-id")

	if source != "" && sourceID != "" {
		builder.FromSource(source, sourceID)
	}
}
