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

package config

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/charmbracelet/huh"
	"github.com/spf13/cobra"

	"git.secluded.site/lunatask-mcp-server/internal/config"
	"git.secluded.site/lunatask-mcp-server/internal/ui"
)

// Port validation errors.
var (
	errPortNotNumber  = errors.New("port must be a number")
	errPortOutOfRange = errors.New("port must be between 1 and 65535")
)

// runServerStep runs the server configuration step.
//
//nolint:cyclop,funlen // wizard flow benefits from being in one function
func runServerStep(cfg *config.Config) wizardNav {
	if cfg.Server.Host == "" {
		cfg.Server.Host = "localhost"
	}

	if cfg.Server.Port == 0 {
		cfg.Server.Port = 8080
	}

	if cfg.Server.Transport == "" {
		cfg.Server.Transport = "stdio"
	}

	if cfg.Timezone == "" {
		cfg.Timezone = "UTC"
	}

	err := huh.NewSelect[string]().
		Title("Transport mode").
		Description("How will clients connect to the server?").
		Options(
			huh.NewOption("stdio (for CLI tools like Crush, Claude Code)", "stdio"),
			huh.NewOption("SSE (for Home Assistant)", "sse"),
			huh.NewOption("HTTP (streamable HTTP transport)", "http"),
		).
		Value(&cfg.Server.Transport).
		Run()
	if err != nil {
		if errors.Is(err, huh.ErrUserAborted) {
			return navQuit
		}

		return navQuit
	}

	// Only ask for host/port if not using stdio
	if cfg.Server.Transport != "stdio" {
		portStr := strconv.Itoa(cfg.Server.Port)

		err = huh.NewForm(
			huh.NewGroup(
				huh.NewInput().
					Title("Host").
					Description("Address to listen on.").
					Placeholder("localhost").
					Value(&cfg.Server.Host),
				huh.NewInput().
					Title("Port").
					Description("Port to listen on.").
					Placeholder("8080").
					Value(&portStr).
					Validate(validatePort),
			),
		).Run()
		if err != nil {
			if errors.Is(err, huh.ErrUserAborted) {
				return navQuit
			}

			return navQuit
		}

		if portStr != "" {
			cfg.Server.Port, _ = strconv.Atoi(portStr)
		}
	}

	err = huh.NewInput().
		Title("Timezone").
		Description("IANA timezone for date parsing (e.g. America/New_York, Europe/London).").
		Placeholder("UTC").
		Value(&cfg.Timezone).
		Run()
	if err != nil {
		if errors.Is(err, huh.ErrUserAborted) {
			return navQuit
		}

		return navQuit
	}

	return navNext
}

// runAreasStep runs the areas configuration step.
func runAreasStep(cfg *config.Config) wizardNav {
	return manageAreasAsStep(cfg)
}

// runHabitsStep runs the habits configuration step.
func runHabitsStep(cfg *config.Config) wizardNav {
	return manageHabitsAsStep(cfg)
}

// runAccessTokenStep runs the access token configuration step.
func runAccessTokenStep(cmd *cobra.Command) wizardNav {
	err := configureAccessToken(cmd)
	if errors.Is(err, errQuit) {
		return navQuit
	}

	if err != nil {
		out := cmd.OutOrStdout()
		_, _ = fmt.Fprintln(out, ui.Error.Render("Token configuration failed: "+err.Error()))

		var skip bool

		skipErr := huh.NewConfirm().
			Title("Skip token configuration?").
			Description("You can configure it later with 'lunatask-mcp-server config'.").
			Affirmative("Skip for now").
			Negative("Go back").
			Value(&skip).
			Run()
		if skipErr != nil {
			return navQuit
		}

		if skip {
			return navNext
		}

		return navBack
	}

	return navNext
}

// configureServer runs the server configuration from the main menu.
func configureServer(cfg *config.Config) error {
	nav := runServerStep(cfg)
	if nav == navQuit {
		return errQuit
	}

	return nil
}

func validatePort(s string) error {
	if s == "" {
		return nil
	}

	p, err := strconv.Atoi(s)
	if err != nil {
		return errPortNotNumber
	}

	if p < 1 || p > 65535 {
		return errPortOutOfRange
	}

	return nil
}
