package main

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

func runLogin(args []string) error {
	fs := flag.NewFlagSet("login", flag.ExitOnError)
	g := addGlobalFlags(fs)
	user := fs.String("user", "", "username (prompted if omitted)")
	pass := fs.String("password", "", "password (insecure; prefer $TINYFORGE_PASSWORD or the prompt)")
	fs.Usage = func() {
		fmt.Fprint(os.Stderr, "Usage: tinyforge login [--user U] [--password P] [--base-url URL]\n\n"+
			"Authenticate against the server and cache the token.\n")
		fs.PrintDefaults()
	}
	if err := fs.Parse(args); err != nil {
		return err
	}

	sess, err := newSession(g)
	if err != nil {
		return err
	}

	username := *user
	if username == "" {
		username, err = promptLine("Username: ")
		if err != nil {
			return err
		}
	}

	password := *pass
	if password == "" {
		password = os.Getenv("TINYFORGE_PASSWORD")
	}
	if password == "" {
		password, err = promptPassword("Password: ")
		if err != nil {
			return err
		}
	}
	if username == "" || password == "" {
		return fmt.Errorf("username and password are required")
	}

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

	var tok SessionToken
	body := map[string]string{"username": username, "password": password}
	if err := sess.client.doJSON(ctx, "POST", "/api/auth/login", body, &tok); err != nil {
		return err
	}

	// Persist the resolved base URL alongside the token so later commands need
	// no flags. The token file is written 0600 by saveConfig.
	sess.cfg.BaseURL = sess.client.baseURL
	sess.cfg.Token = tok.Token
	sess.cfg.ExpiresAt = tok.ExpiresAt
	if err := saveConfig(sess.configPath, sess.cfg); err != nil {
		return err
	}

	fmt.Printf("Logged in to %s as %s.\n", sess.client.baseURL, username)
	if exp := friendlyExpiry(tok.ExpiresAt); exp != "" {
		fmt.Printf("Token valid until %s.\n", exp)
	}
	return nil
}

func runLogout(args []string) error {
	fs := flag.NewFlagSet("logout", flag.ExitOnError)
	g := addGlobalFlags(fs)
	if err := fs.Parse(args); err != nil {
		return err
	}

	sess, err := newSession(g)
	if err != nil {
		return err
	}
	if sess.client.token == "" {
		fmt.Println("Not logged in.")
		return nil
	}

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

	// Best-effort server-side revocation; clear the local token regardless.
	revokeErr := sess.client.doJSON(ctx, "POST", "/api/auth/logout", nil, nil)

	sess.cfg.Token = ""
	sess.cfg.ExpiresAt = ""
	if err := saveConfig(sess.configPath, sess.cfg); err != nil {
		return err
	}

	if revokeErr != nil {
		fmt.Printf("Cleared local token (server revocation skipped: %v).\n", revokeErr)
		return nil
	}
	fmt.Println("Logged out.")
	return nil
}

// promptLine reads a single trimmed line from stdin.
func promptLine(label string) (string, error) {
	fmt.Fprint(os.Stderr, label)
	r := bufio.NewReader(os.Stdin)
	line, err := r.ReadString('\n')
	if err != nil && line == "" {
		return "", fmt.Errorf("read input: %w", err)
	}
	return strings.TrimSpace(line), nil
}

// friendlyExpiry formats an RFC3339 expiry as a local time, best-effort.
func friendlyExpiry(s string) string {
	if s == "" {
		return ""
	}
	t, err := time.Parse(time.RFC3339, s)
	if err != nil {
		return s
	}
	return t.Local().Format("2006-01-02 15:04 MST")
}
