package cmd

import (
	"context"
	"encoding/json"
	"fmt"
	"os"

	"gogs.dmsc.dev/arp/arp_cli/client"
	"gogs.dmsc.dev/arp/arp_cli/config"

	"github.com/AlecAivazis/survey/v2"
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli/v3"
)

// NoteCommand returns the note command
func NoteCommand() *cli.Command {
	return &cli.Command{
		Name:  "note",
		Usage: "Manage notes",
		Description: `Manage ARP notes. Notes are text entries belonging to users and associated with services.

Use this command to create, list, update, and delete notes.`,
		Commands: []*cli.Command{
			{
				Name:    "list",
				Aliases: []string{"ls"},
				Usage:   "List all notes",
				Flags: []cli.Flag{
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: noteList,
			},
			{
				Name:  "get",
				Usage: "Get a note by ID",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Note ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: noteGet,
			},
			{
				Name:   "create",
				Usage:  "Create a new note",
				Action: noteCreate,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "title",
						Aliases: []string{"t"},
						Usage:   "Note title",
					},
					&cli.StringFlag{
						Name:    "content",
						Aliases: []string{"c"},
						Usage:   "Note content",
					},
					&cli.StringFlag{
						Name:    "user",
						Aliases: []string{"u"},
						Usage:   "User ID",
					},
					&cli.StringFlag{
						Name:    "service",
						Aliases: []string{"s"},
						Usage:   "Service ID",
					},
				},
			},
			{
				Name:   "update",
				Usage:  "Update a note",
				Action: noteUpdate,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Note ID",
						Required: true,
					},
					&cli.StringFlag{
						Name:    "title",
						Aliases: []string{"t"},
						Usage:   "Note title",
					},
					&cli.StringFlag{
						Name:    "content",
						Aliases: []string{"c"},
						Usage:   "Note content",
					},
					&cli.StringFlag{
						Name:    "user",
						Aliases: []string{"u"},
						Usage:   "User ID",
					},
					&cli.StringFlag{
						Name:    "service",
						Aliases: []string{"s"},
						Usage:   "Service ID",
					},
				},
			},
			{
				Name:   "delete",
				Usage:  "Delete a note",
				Action: noteDelete,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Note ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "yes",
						Aliases: []string{"y"},
						Usage:   "Skip confirmation",
					},
				},
			},
		},
	}
}

type Note struct {
	ID        string   `json:"id"`
	Title     string   `json:"title"`
	Content   string   `json:"content"`
	UserID    string   `json:"userId"`
	User      *User    `json:"user"`
	ServiceID string   `json:"serviceId"`
	Service   *Service `json:"service"`
	CreatedAt string   `json:"createdAt"`
	UpdatedAt string   `json:"updatedAt"`
}

func noteList(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	query := "query Notes { notes { id title content userId user { id email } serviceId createdAt updatedAt } }"

	resp, err := c.Query(query, nil)
	if err != nil {
		return err
	}

	var result struct {
		Notes []Note `json:"notes"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if cmd.Bool("json") {
		enc := json.NewEncoder(os.Stdout)
		enc.SetIndent("", "  ")
		return enc.Encode(result.Notes)
	}

	if len(result.Notes) == 0 {
		fmt.Println("No notes found.")
		return nil
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.Header([]string{"ID", "Title", "User", "Service ID", "Created At"})
	

	for _, n := range result.Notes {
		userEmail := ""
		if n.User != nil {
			userEmail = n.User.Email
		}
		table.Append([]string{n.ID, n.Title, userEmail, n.ServiceID, n.CreatedAt})
	}

	table.Render()
	return nil
}

func noteGet(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	id := cmd.String("id")
	query := "query Note($id: ID!) { note(id: $id) { id title content userId user { id email } serviceId service { id name } createdAt updatedAt } }"

	resp, err := c.Query(query, map[string]interface{}{"id": id})
	if err != nil {
		return err
	}

	var result struct {
		Note *Note `json:"note"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.Note == nil {
		return fmt.Errorf("note not found")
	}

	if cmd.Bool("json") {
		enc := json.NewEncoder(os.Stdout)
		enc.SetIndent("", "  ")
		return enc.Encode(result.Note)
	}

	n := result.Note
	fmt.Printf("ID: %s\n", n.ID)
	fmt.Printf("Title: %s\n", n.Title)
	fmt.Printf("Content: %s\n", n.Content)
	if n.User != nil {
		fmt.Printf("User: %s (%s)\n", n.User.Email, n.UserID)
	} else {
		fmt.Printf("User ID: %s\n", n.UserID)
	}
	if n.Service != nil {
		fmt.Printf("Service: %s (%s)\n", n.Service.Name, n.ServiceID)
	} else {
		fmt.Printf("Service ID: %s\n", n.ServiceID)
	}
	fmt.Printf("Created At: %s\n", n.CreatedAt)
	fmt.Printf("Updated At: %s\n", n.UpdatedAt)

	return nil
}

func noteCreate(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	title := cmd.String("title")
	content := cmd.String("content")
	userID := cmd.String("user")
	serviceID := cmd.String("service")

	if title == "" {
		prompt := &survey.Input{Message: "Title:"}
		if err := survey.AskOne(prompt, &title, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	if content == "" {
		prompt := &survey.Multiline{Message: "Content:"}
		if err := survey.AskOne(prompt, &content, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	if userID == "" {
		prompt := &survey.Input{Message: "User ID:"}
		if err := survey.AskOne(prompt, &userID, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	if serviceID == "" {
		prompt := &survey.Input{Message: "Service ID:"}
		if err := survey.AskOne(prompt, &serviceID, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	mutation := `mutation CreateNote($input: NewNote!) { createNote(input: $input) { id title content userId user { id email } serviceId createdAt updatedAt } }`

	input := map[string]interface{}{
		"title":     title,
		"content":   content,
		"userId":    userID,
		"serviceId": serviceID,
	}

	resp, err := c.Mutation(mutation, map[string]interface{}{"input": input})
	if err != nil {
		return err
	}

	var result struct {
		CreateNote *Note `json:"createNote"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.CreateNote == nil {
		return fmt.Errorf("failed to create note")
	}

	fmt.Printf("Note created successfully!\n")
	fmt.Printf("ID: %s\n", result.CreateNote.ID)
	fmt.Printf("Title: %s\n", result.CreateNote.Title)

	return nil
}

func noteUpdate(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	id := cmd.String("id")
	title := cmd.String("title")
	content := cmd.String("content")
	userID := cmd.String("user")
	serviceID := cmd.String("service")

	if title == "" && content == "" && userID == "" && serviceID == "" {
		fmt.Println("No updates provided. Use flags to specify what to update.")
		return nil
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	input := make(map[string]interface{})
	if title != "" {
		input["title"] = title
	}
	if content != "" {
		input["content"] = content
	}
	if userID != "" {
		input["userId"] = userID
	}
	if serviceID != "" {
		input["serviceId"] = serviceID
	}

	mutation := `mutation UpdateNote($id: ID!, $input: UpdateNoteInput!) { updateNote(id: $id, input: $input) { id title content userId serviceId createdAt updatedAt } }`

	resp, err := c.Mutation(mutation, map[string]interface{}{"id": id, "input": input})
	if err != nil {
		return err
	}

	var result struct {
		UpdateNote *Note `json:"updateNote"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.UpdateNote == nil {
		return fmt.Errorf("note not found")
	}

	fmt.Printf("Note updated successfully!\n")
	fmt.Printf("ID: %s\n", result.UpdateNote.ID)
	fmt.Printf("Title: %s\n", result.UpdateNote.Title)

	return nil
}

func noteDelete(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	id := cmd.String("id")
	skipConfirm := cmd.Bool("yes")

	if !skipConfirm {
		confirm := false
		prompt := &survey.Confirm{
			Message: fmt.Sprintf("Are you sure you want to delete note %s?", id),
			Default: false,
		}
		if err := survey.AskOne(prompt, &confirm); err != nil {
			return err
		}
		if !confirm {
			fmt.Println("Deletion cancelled.")
			return nil
		}
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	mutation := `mutation DeleteNote($id: ID!) { deleteNote(id: $id) }`

	resp, err := c.Mutation(mutation, map[string]interface{}{"id": id})
	if err != nil {
		return err
	}

	var result struct {
		DeleteNote bool `json:"deleteNote"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.DeleteNote {
		fmt.Printf("Note %s deleted successfully.\n", id)
	} else {
		fmt.Printf("Failed to delete note %s.\n", id)
	}

	return nil
}
