package main

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/sorti/openspeak/internal/client"
	pb "github.com/sorti/openspeak/pkg/api/openspeak/v1"
)

// ClientApp represents the CLI client application
type ClientApp struct {
	grpcClient       *client.GRPCClient
	currentUser      string
	currentChannelID string
	channels         []*pb.Channel
	selectedChannelIdx int
	micMuted         bool
	speakerMuted     bool
	running          bool
}

func main() {
	host := flag.String("host", "localhost", "gRPC server host")
	port := flag.Int("port", 50051, "gRPC server port")
	flag.Parse()

	app := &ClientApp{
		running: true,
	}

	fmt.Println("╔════════════════════════════════════════╗")
	fmt.Println("║       OpenSpeak - CLI Client           ║")
	fmt.Println("║   Voice Communication Platform         ║")
	fmt.Println("╚════════════════════════════════════════╝")
	fmt.Println()

	// Login
	if !app.login(host, port) {
		fmt.Println("Failed to login. Exiting.")
		return
	}

	fmt.Printf("✓ Logged in as: %s\n", app.currentUser)

	// Load channels
	if !app.loadChannels() {
		fmt.Println("Failed to load channels.")
		return
	}

	// Main REPL loop
	app.mainLoop()

	// Cleanup
	if app.grpcClient != nil {
		app.grpcClient.Close()
	}
}

// login connects to the server and authenticates
func (app *ClientApp) login(host *string, port *int) bool {
	reader := bufio.NewReader(os.Stdin)

	// Get token from user
	fmt.Print("Enter admin token: ")
	token, _ := reader.ReadString('\n')
	token = strings.TrimSpace(token)

	if token == "" {
		fmt.Println("Token cannot be empty")
		return false
	}

	// Connect to gRPC server
	fmt.Printf("Connecting to %s:%d...\n", *host, *port)
	grpcClient, err := client.NewGRPCClient(*host, *port)
	if err != nil {
		fmt.Printf("Connection failed: %v\n", err)
		return false
	}

	// Authenticate
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	loginResp, err := grpcClient.Login(ctx, token)
	if err != nil {
		fmt.Printf("Authentication failed: %v\n", err)
		grpcClient.Close()
		return false
	}

	app.grpcClient = grpcClient
	app.currentUser = loginResp.UserId
	return true
}

// loadChannels fetches the channel list from the server
func (app *ClientApp) loadChannels() bool {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	resp, err := app.grpcClient.ListChannels(ctx)
	if err != nil {
		fmt.Printf("Failed to load channels: %v\n", err)
		return false
	}

	app.channels = resp.Channels
	fmt.Printf("✓ Loaded %d channels\n", len(app.channels))
	return true
}

// mainLoop runs the interactive command loop
func (app *ClientApp) mainLoop() {
	reader := bufio.NewReader(os.Stdin)

	fmt.Println()
	fmt.Println("Commands: list, select, join, leave, members, mute, status, help, quit")
	fmt.Println()

	for app.running {
		fmt.Print("> ")
		input, _ := reader.ReadString('\n')
		input = strings.TrimSpace(input)

		if input == "" {
			continue
		}

		parts := strings.Fields(input)
		cmd := strings.ToLower(parts[0])

		switch cmd {
		case "list":
			app.listChannels()
		case "select":
			if len(parts) > 1 {
				app.selectChannel(parts[1])
			} else {
				fmt.Println("Usage: select <channel_index>")
			}
		case "join":
			app.joinChannel()
		case "leave":
			app.leaveChannel()
		case "members":
			app.listMembers()
		case "mute":
			app.toggleMute()
		case "status":
			app.showStatus()
		case "help":
			app.showHelp()
		case "quit", "exit":
			app.running = false
		default:
			fmt.Printf("Unknown command: %s. Type 'help' for available commands.\n", cmd)
		}
	}
}

// listChannels displays available channels
func (app *ClientApp) listChannels() {
	if len(app.channels) == 0 {
		fmt.Println("No channels available")
		return
	}

	fmt.Println("\nAvailable Channels:")
	fmt.Println("─────────────────────────────────────────")
	for i, ch := range app.channels {
		marker := " "
		if i == app.selectedChannelIdx {
			marker = "►"
		}
		fmt.Printf("%s [%d] %s (%d members)\n", marker, i, ch.Name, len(ch.MemberIds))
	}
	fmt.Println()
}

// selectChannel selects a channel for operations
func (app *ClientApp) selectChannel(indexStr string) {
	index, err := strconv.Atoi(indexStr)
	if err != nil || index < 0 || index >= len(app.channels) {
		fmt.Println("Invalid channel index")
		return
	}

	app.selectedChannelIdx = index
	ch := app.channels[index]
	fmt.Printf("✓ Selected channel: %s\n", ch.Name)
}

// joinChannel joins the selected channel
func (app *ClientApp) joinChannel() {
	if app.selectedChannelIdx < 0 || app.selectedChannelIdx >= len(app.channels) {
		fmt.Println("No channel selected. Use 'select' command first.")
		return
	}

	ch := app.channels[app.selectedChannelIdx]

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	_, err := app.grpcClient.JoinChannel(ctx, ch.Id)
	if err != nil {
		fmt.Printf("✗ Failed to join channel: %v\n", err)
		return
	}

	app.currentChannelID = ch.Id
	fmt.Printf("✓ Joined channel: %s\n", ch.Name)
}

// leaveChannel leaves the current channel
func (app *ClientApp) leaveChannel() {
	if app.currentChannelID == "" {
		fmt.Println("Not in any channel")
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	_, err := app.grpcClient.LeaveChannel(ctx, app.currentChannelID)
	if err != nil {
		fmt.Printf("✗ Failed to leave channel: %v\n", err)
		return
	}

	channelName := ""
	for _, ch := range app.channels {
		if ch.Id == app.currentChannelID {
			channelName = ch.Name
			break
		}
	}

	app.currentChannelID = ""
	fmt.Printf("✓ Left channel: %s\n", channelName)
}

// listMembers displays members of the selected channel
func (app *ClientApp) listMembers() {
	if app.selectedChannelIdx < 0 || app.selectedChannelIdx >= len(app.channels) {
		fmt.Println("No channel selected. Use 'select' command first.")
		return
	}

	ch := app.channels[app.selectedChannelIdx]

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	resp, err := app.grpcClient.ListMembers(ctx, ch.Id)
	if err != nil {
		fmt.Printf("✗ Failed to load members: %v\n", err)
		return
	}

	fmt.Printf("\nMembers of '%s':\n", ch.Name)
	fmt.Println("─────────────────────────────────────────")
	if len(resp.MemberIds) == 0 {
		fmt.Println("No members")
	} else {
		for i, member := range resp.MemberIds {
			fmt.Printf("%d. %s\n", i+1, member)
		}
	}
	fmt.Println()
}

// toggleMute toggles microphone mute status
func (app *ClientApp) toggleMute() {
	app.micMuted = !app.micMuted

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	_, err := app.grpcClient.SetMuteStatus(ctx, app.micMuted, app.speakerMuted)
	if err != nil {
		fmt.Printf("✗ Failed to set mute status: %v\n", err)
		return
	}

	status := "unmuted"
	if app.micMuted {
		status = "muted"
	}
	fmt.Printf("✓ Microphone %s\n", status)
}

// showStatus shows the current connection status
func (app *ClientApp) showStatus() {
	fmt.Println("\nCurrent Status:")
	fmt.Println("─────────────────────────────────────────")
	fmt.Printf("User: %s\n", app.currentUser)

	if app.currentChannelID != "" {
		for _, ch := range app.channels {
			if ch.Id == app.currentChannelID {
				fmt.Printf("Current Channel: %s\n", ch.Name)
				break
			}
		}
	} else {
		fmt.Println("Current Channel: None")
	}

	micStatus := "🎤 Unmuted"
	if app.micMuted {
		micStatus = "🔇 Muted"
	}
	fmt.Printf("Microphone: %s\n", micStatus)

	speakerStatus := "🔊 On"
	if app.speakerMuted {
		speakerStatus = "🔇 Off"
	}
	fmt.Printf("Speaker: %s\n", speakerStatus)
	fmt.Println()
}

// showHelp displays available commands
func (app *ClientApp) showHelp() {
	fmt.Println("\nAvailable Commands:")
	fmt.Println("─────────────────────────────────────────")
	fmt.Println("list              - Show all available channels")
	fmt.Println("select <idx>      - Select a channel (e.g., 'select 0')")
	fmt.Println("join              - Join the selected channel")
	fmt.Println("leave             - Leave the current channel")
	fmt.Println("members           - List members of selected channel")
	fmt.Println("mute              - Toggle microphone mute")
	fmt.Println("status            - Show current connection status")
	fmt.Println("help              - Show this help message")
	fmt.Println("quit/exit         - Exit the application")
	fmt.Println()
}

// Helper to access selected channel
func (app *ClientApp) selectedChannel() *pb.Channel {
	if app.selectedChannelIdx >= 0 && app.selectedChannelIdx < len(app.channels) {
		return app.channels[app.selectedChannelIdx]
	}
	return nil
}
