package cmd

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

	"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"
)

// MessageCommand returns the message command
func MessageCommand() *cli.Command {
	return &cli.Command{
		Name:  "message",
		Usage: "Manage messages",
		Description: `Manage ARP messages. Messages are sent between users/agents.

Use this command to create, list, update, and delete messages. You can also watch for real-time message updates.`,
		Commands: []*cli.Command{
			{
				Name:    "list",
				Aliases: []string{"ls"},
				Usage:   "List all messages",
				Flags: []cli.Flag{
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: messageList,
			},
			{
				Name:  "get",
				Usage: "Get a message by ID",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Message ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: messageGet,
			},
			{
				Name:   "create",
				Usage:  "Create a new message",
				Action: messageCreate,
				Flags: []cli.Flag{
					&cli.StringSliceFlag{
						Name:    "receivers",
						Aliases: []string{"r"},
						Usage:   "Receiver user IDs (comma-separated or multiple flags)",
					},
					&cli.StringFlag{
						Name:    "content",
						Aliases: []string{"m"},
						Usage:   "Message content",
					},
				},
			},
			{
				Name:   "update",
				Usage:  "Update a message",
				Action: messageUpdate,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Message ID",
						Required: true,
					},
					&cli.StringFlag{
						Name:    "content",
						Aliases: []string{"c"},
						Usage:   "Message content",
					},
					&cli.StringSliceFlag{
						Name:    "receivers",
						Aliases: []string{"r"},
						Usage:   "Receiver user IDs (comma-separated or multiple flags)",
					},
				},
			},
			{
				Name:   "delete",
				Usage:  "Delete a message",
				Action: messageDelete,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Message ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "yes",
						Aliases: []string{"y"},
						Usage:   "Skip confirmation",
					},
				},
			},
			{
				Name:   "watch",
				Usage:  "Watch for real-time message updates",
				Action: messageWatch,
			},
		},
	}
}

type Message struct {
	ID              string   `json:"id"`
	SenderID        string   `json:"senderId"`
	Sender          *User    `json:"sender"`
	Content         string   `json:"content"`
	SentAt          string   `json:"sentAt"`
	Receivers       []string `json:"receivers"`
	ReceiverObjects []*User  `json:"receiverObjects"`
	CreatedAt       string   `json:"createdAt"`
	UpdatedAt       string   `json:"updatedAt"`
}

func messageList(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 Messages { messages { id sender { id email } content sentAt receivers receiverObjects { id email } createdAt } }"

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

	var result struct {
		Messages []Message `json:"messages"`
	}
	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.Messages)
	}

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

	table := tablewriter.NewWriter(os.Stdout)
	table.Header([]string{"ID", "Sender", "Content", "Receivers", "Sent At"})

	for _, m := range result.Messages {
		sender := ""
		if m.Sender != nil {
			sender = m.Sender.Email
		}
		content := m.Content
		if len(content) > 50 {
			content = content[:47] + "..."
		}

		// Build receiver emails string
		var receiverEmails []string
		if len(m.ReceiverObjects) > 0 {
			for _, receiver := range m.ReceiverObjects {
				if receiver != nil {
					receiverEmails = append(receiverEmails, receiver.Email)
				}
			}
		} else {
			// Fallback to IDs if objects aren't available
			receiverEmails = m.Receivers
		}
		receivers := strings.Join(receiverEmails, ", ")

		table.Append([]string{m.ID, sender, content, receivers, m.SentAt})
	}

	table.Render()
	return nil
}

func messageGet(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 Message($id: ID!) { message(id: $id) { id sender { id email } content sentAt receivers receiverObjects { id email } createdAt updatedAt } }"

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

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

	if result.Message == nil {
		return fmt.Errorf("message not found")
	}

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

	m := result.Message
	fmt.Printf("ID: %s\n", m.ID)
	if m.Sender != nil {
		fmt.Printf("Sender: %s (%s)\n", m.Sender.Email, m.SenderID)
	} else {
		fmt.Printf("Sender ID: %s\n", m.SenderID)
	}
	fmt.Printf("Content: %s\n", m.Content)

	// Display receiver emails
	var receiverEmails []string
	if len(m.ReceiverObjects) > 0 {
		for _, receiver := range m.ReceiverObjects {
			if receiver != nil {
				receiverEmails = append(receiverEmails, receiver.Email)
			}
		}
	} else {
		// Fallback to IDs if objects aren't available
		receiverEmails = m.Receivers
	}
	fmt.Printf("Receivers: %s\n", strings.Join(receiverEmails, ", "))

	fmt.Printf("Sent At: %s\n", m.SentAt)
	fmt.Printf("Created At: %s\n", m.CreatedAt)
	fmt.Printf("Updated At: %s\n", m.UpdatedAt)

	return nil
}

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

	receivers := cmd.StringSlice("receivers")
	content := cmd.String("content")

	if len(receivers) == 0 {
		var receiverInput string
		prompt := &survey.Input{Message: "Receiver user IDs (comma-separated):"}
		if err := survey.AskOne(prompt, &receiverInput, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
		receivers = strings.Split(receiverInput, ",")
		for i, r := range receivers {
			receivers[i] = strings.TrimSpace(r)
		}
	}

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

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

	mutation := `mutation CreateMessage($input: NewMessage!) { createMessage(input: $input) { id sender { id email } content sentAt receivers createdAt } }`

	input := map[string]interface{}{
		"content":   content,
		"receivers": receivers,
	}

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

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

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

	fmt.Printf("Message created successfully!\n")
	fmt.Printf("ID: %s\n", result.CreateMessage.ID)

	return nil
}

func messageUpdate(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")
	content := cmd.String("content")
	receivers := cmd.StringSlice("receivers")

	if content == "" && len(receivers) == 0 {
		prompt := &survey.Multiline{Message: "Message content:"}
		if err := survey.AskOne(prompt, &content); err != nil {
			return err
		}
	}

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

	mutation := `mutation UpdateMessage($id: ID!, $input: UpdateMessage!) { updateMessage(id: $id, input: $input) { id sender { id email } content sentAt receivers createdAt updatedAt } }`

	input := map[string]interface{}{}
	if content != "" {
		input["content"] = content
	}
	if len(receivers) > 0 {
		input["receivers"] = receivers
	}

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

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

	if result.UpdateMessage == nil {
		return fmt.Errorf("failed to update message")
	}

	fmt.Printf("Message updated successfully!\n")
	fmt.Printf("ID: %s\n", result.UpdateMessage.ID)

	return nil
}

func messageDelete(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")

	if !cmd.Bool("yes") {
		confirm := false
		prompt := &survey.Confirm{Message: fmt.Sprintf("Are you sure you want to delete message %s?", id)}
		if err := survey.AskOne(prompt, &confirm); err != nil {
			return err
		}
		if !confirm {
			fmt.Println("Cancelled.")
			return nil
		}
	}

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

	mutation := `mutation DeleteMessage($id: ID!) { deleteMessage(id: $id) { id } }`

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

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

	if result.DeleteMessage == nil {
		return fmt.Errorf("failed to delete message")
	}

	fmt.Printf("Message %s deleted successfully!\n", result.DeleteMessage.ID)

	return nil
}

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

	wsClient := client.NewWebSocketClient(cfg.ServerURL, cfg.Token)
	if err := wsClient.Connect(); err != nil {
		return fmt.Errorf("failed to connect: %w", err)
	}
	defer wsClient.Close()

	subscription := `subscription MessageAdded { messageAdded { id sender { id email } content sentAt receivers receiverObjects { id email } createdAt } }`

	if err := wsClient.Subscribe("1", subscription, nil); err != nil {
		return fmt.Errorf("failed to subscribe: %w", err)
	}

	fmt.Println("Watching for new messages... (press Ctrl+C to stop)")

	for {
		select {
		case msg := <-wsClient.Messages():
			var result struct {
				MessageAdded *Message `json:"messageAdded"`
			}
			if err := json.Unmarshal(msg, &result); err != nil {
				fmt.Fprintf(os.Stderr, "Error parsing message: %v\n", err)
				continue
			}

			if result.MessageAdded != nil {
				m := result.MessageAdded
				sender := "Unknown"
				if m.Sender != nil {
					sender = m.Sender.Email
				}

				// Build receiver emails string
				var receiverEmails []string
				if len(m.ReceiverObjects) > 0 {
					for _, receiver := range m.ReceiverObjects {
						if receiver != nil {
							receiverEmails = append(receiverEmails, receiver.Email)
						}
					}
				} else {
					// Fallback to IDs if objects aren't available
					receiverEmails = m.Receivers
				}

				fmt.Printf("\n--- New Message ---\n")
				fmt.Printf("ID: %s\n", m.ID)
				fmt.Printf("From: %s\n", sender)
				fmt.Printf("Content: %s\n", m.Content)
				fmt.Printf("Receivers: %s\n", strings.Join(receiverEmails, ", "))
				fmt.Printf("Sent At: %s\n", m.SentAt)
				fmt.Println("--------------------")
			}
		case err := <-wsClient.Errors():
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		case <-wsClient.Done():
			return nil
		case <-ctx.Done():
			return nil
		}
	}
}
