package main

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"golang.org/x/term"

	"github.com/sartoopjj/thefeed/internal/server"
	"github.com/sartoopjj/thefeed/internal/version"
)

func main() {
	dataDir := flag.String("data-dir", "./data", "Data directory for channels, session, and config")
	listen := flag.String("listen", ":53", "DNS listen address (host:port)")
	domain := flag.String("domain", "", "DNS domain (e.g., t.example.com)")
	key := flag.String("key", "", "Encryption passphrase")
	channelsFile := flag.String("channels", "", "Path to channels file (default: {data-dir}/channels.txt)")
	xAccountsFile := flag.String("x-accounts", "", "Path to X accounts file (default: {data-dir}/x_accounts.txt)")
	xRSSInstances := flag.String("x-rss-instances", "", "Comma-separated X RSS base URLs (e.g., https://nitter.net,http://nitter.net)")
	apiID := flag.String("api-id", "", "Telegram API ID (optional if --no-telegram)")
	apiHash := flag.String("api-hash", "", "Telegram API Hash (optional if --no-telegram)")
	phone := flag.String("phone", "", "Telegram phone number (optional if --no-telegram)")
	loginOnly := flag.Bool("login-only", false, "Authenticate to Telegram, save session, and exit")
	noTelegram := flag.Bool("no-telegram", false, "Fetch public channels without Telegram login")
	sessionPath := flag.String("session", "", "Path to Telegram session file (default: {data-dir}/session.json)")
	maxPadding := flag.Int("padding", 32, "Max random padding bytes in DNS responses (anti-DPI, 0=disabled)")
	msgLimit := flag.Int("msg-limit", 15, "Maximum messages to fetch per Telegram channel")
	fetchIntervalMin := flag.Int("fetch-interval", 10, "Fetch cycle interval in minutes (min 3, default 10)")
	allowManage := flag.Bool("allow-manage", false, "Allow remote channel management and sending via DNS")
	debug := flag.Bool("debug", false, "Log every decoded DNS query")
	dnsMediaEnabled := flag.Bool("dns-media-enabled", false, "Serve media via DNS (slow relay)")
	dnsMediaMaxSizeKB := flag.Int("dns-media-max-size", 100, "Per-file cap for the DNS relay in KB (0 = no cap)")
	dnsMediaCacheTTLMin := flag.Int("dns-media-cache-ttl", 600, "TTL for DNS-relay cached media, in minutes")
	dnsMediaCompression := flag.String("dns-media-compression", "gzip", "Compression for DNS-relay media bytes: none|gzip|deflate")
	ghEnabled := flag.Bool("github-relay-enabled", false, "Serve media via GitHub (fast relay)")
	ghToken := flag.String("github-relay-token", "", "GitHub PAT with contents:write on the relay repo")
	ghRepo := flag.String("github-relay-repo", "", "GitHub repo for the fast relay, e.g. owner/repo")
	ghBranch := flag.String("github-relay-branch", "main", "Default branch to commit to (e.g. main, master)")
	ghMaxSizeKB := flag.Int("github-relay-max-size", 15*1024, "Per-file cap for the GitHub relay in KB (0 = no cap)")
	ghCacheTTLMin := flag.Int("github-relay-ttl", 600, "TTL for GitHub-relay objects in minutes")
	showVersion := flag.Bool("version", false, "Show version and exit")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "thefeed-server %s\n\nServes Telegram/X feed content over encrypted DNS for censorship-resistant access.\n\nUsage:\n  thefeed-server [flags]\n\nFlags:\n", version.Version)
		flag.PrintDefaults()
	}
	flag.Parse()

	if *showVersion {
		fmt.Printf("thefeed-server %s (commit: %s, built: %s)\n", version.Version, version.Commit, version.Date)
		os.Exit(0)
	}

	// Catch the common --bool true mistake: Go's flag package stops parsing at
	// the first positional, so any flags after it are silently dropped. Bool
	// flags must use --foo or --foo=true (no space).
	if flag.NArg() > 0 {
		fmt.Fprintf(os.Stderr, "Error: unexpected positional argument(s): %v\n", flag.Args())
		fmt.Fprintln(os.Stderr, "Hint: bool flags must be written as --flag or --flag=true (NOT --flag true).")
		os.Exit(1)
	}

	// Create data directory
	if err := os.MkdirAll(*dataDir, 0700); err != nil {
		log.Fatalf("Create data dir: %v", err)
	}

	// Default paths relative to data directory
	if *channelsFile == "" {
		*channelsFile = filepath.Join(*dataDir, "channels.txt")
	}
	if *xAccountsFile == "" {
		*xAccountsFile = filepath.Join(*dataDir, "x_accounts.txt")
	}
	if *sessionPath == "" {
		*sessionPath = filepath.Join(*dataDir, "session.json")
	}

	if *domain == "" {
		*domain = os.Getenv("THEFEED_DOMAIN")
	}
	if *key == "" {
		*key = os.Getenv("THEFEED_KEY")
	}
	if !*allowManage && os.Getenv("THEFEED_ALLOW_MANAGE") == "1" {
		*allowManage = true
	}
	// THEFEED_ALLOW_MANAGE=0 explicitly disables, even if flag was set
	if os.Getenv("THEFEED_ALLOW_MANAGE") == "0" {
		*allowManage = false
	}
	if *fetchIntervalMin == 10 {
		if v := os.Getenv("THEFEED_FETCH_INTERVAL"); v != "" {
			if n, err := strconv.Atoi(v); err == nil && n > 0 {
				*fetchIntervalMin = n
			}
		}
	}
	if *fetchIntervalMin < 3 {
		fmt.Fprintf(os.Stderr, "Error: --fetch-interval must be at least 3 minutes (got %d)\n", *fetchIntervalMin)
		os.Exit(1)
	}
	if *msgLimit == 15 {
		if v := os.Getenv("THEFEED_MSG_LIMIT"); v != "" {
			if n, err := strconv.Atoi(v); err == nil && n > 0 {
				*msgLimit = n
			}
		}
	}
	if *apiID == "" {
		*apiID = os.Getenv("TELEGRAM_API_ID")
	}
	if *apiHash == "" {
		*apiHash = os.Getenv("TELEGRAM_API_HASH")
	}
	if *phone == "" {
		*phone = os.Getenv("TELEGRAM_PHONE")
	}
	if *xRSSInstances == "" {
		*xRSSInstances = os.Getenv("THEFEED_X_RSS_INSTANCES")
	}

	if *domain == "" || *key == "" {
		fmt.Fprintln(os.Stderr, "Error: --domain and --key are required")
		flag.Usage()
		os.Exit(1)
	}

	// Telegram credentials are required unless --no-telegram
	needTelegram := !*noTelegram
	if needTelegram {
		if *apiID == "" || *apiHash == "" || *phone == "" {
			fmt.Fprintln(os.Stderr, "Error: --api-id, --api-hash, and --phone are required (use --no-telegram to skip)")
			flag.Usage()
			os.Exit(1)
		}
	}

	var id int
	if *apiID != "" {
		var err error
		id, err = strconv.Atoi(*apiID)
		if err != nil {
			log.Fatalf("Invalid API ID: %v", err)
		}
	}

	// Interactive 2FA password prompt — only when Telegram is enabled
	password := os.Getenv("TELEGRAM_PASSWORD")
	if password == "" && needTelegram {
		hasSession := false
		if info, statErr := os.Stat(*sessionPath); statErr == nil && info.Size() > 0 {
			hasSession = true
		}
		if *loginOnly || !hasSession {
			fmt.Print("Telegram 2FA password (press Enter if none): ")
			pwBytes, err := term.ReadPassword(int(syscall.Stdin))
			fmt.Println()
			if err == nil && len(pwBytes) > 0 {
				password = string(pwBytes)
			}
		}
	}

	if env := os.Getenv("THEFEED_DNS_MEDIA_ENABLED"); env == "0" {
		*dnsMediaEnabled = false
	} else if env == "1" {
		*dnsMediaEnabled = true
	}
	if env := os.Getenv("THEFEED_DNS_MEDIA_MAX_SIZE_KB"); env != "" {
		if n, err := strconv.Atoi(env); err == nil {
			*dnsMediaMaxSizeKB = n
		}
	}
	if env := os.Getenv("THEFEED_DNS_MEDIA_CACHE_TTL_MIN"); env != "" {
		if n, err := strconv.Atoi(env); err == nil {
			*dnsMediaCacheTTLMin = n
		}
	}
	if env := os.Getenv("THEFEED_DNS_MEDIA_COMPRESSION"); env != "" {
		*dnsMediaCompression = env
	}
	if !*ghEnabled && os.Getenv("THEFEED_GITHUB_RELAY_ENABLED") == "1" {
		*ghEnabled = true
	}
	if *ghToken == "" {
		*ghToken = os.Getenv("THEFEED_GITHUB_RELAY_TOKEN")
	}
	if *ghRepo == "" {
		*ghRepo = os.Getenv("THEFEED_GITHUB_RELAY_REPO")
	}
	if *ghBranch == "main" {
		if v := os.Getenv("THEFEED_GITHUB_RELAY_BRANCH"); v != "" {
			*ghBranch = v
		}
	}
	if env := os.Getenv("THEFEED_GITHUB_RELAY_MAX_SIZE_KB"); env != "" {
		if n, err := strconv.Atoi(env); err == nil {
			*ghMaxSizeKB = n
		}
	}
	if env := os.Getenv("THEFEED_GITHUB_RELAY_TTL_MIN"); env != "" {
		if n, err := strconv.Atoi(env); err == nil {
			*ghCacheTTLMin = n
		}
	}

	cfg := server.Config{
		ListenAddr:       *listen,
		Domain:           *domain,
		Passphrase:       *key,
		ChannelsFile:     *channelsFile,
		XAccountsFile:    *xAccountsFile,
		XRSSInstances:    *xRSSInstances,
		MaxPadding:       *maxPadding,
		MsgLimit:         *msgLimit,
		NoTelegram:       *noTelegram,
		AllowManage:      *allowManage,
		Debug:            *debug,
		DNSMediaEnabled:     *dnsMediaEnabled,
		DNSMediaMaxSize:     int64(*dnsMediaMaxSizeKB) * 1024,
		DNSMediaCacheTTL:    *dnsMediaCacheTTLMin,
		DNSMediaCompression: *dnsMediaCompression,
		FetchInterval:       time.Duration(*fetchIntervalMin) * time.Minute,
		GitHubRelay: server.GitHubRelayConfig{
			Enabled:    *ghEnabled,
			Token:      *ghToken,
			Repo:       *ghRepo,
			Branch:     *ghBranch,
			StatePath:  filepath.Join(*dataDir, "gh_relay_state.json"),
			MaxBytes:   int64(*ghMaxSizeKB) * 1024,
			TTLMinutes: *ghCacheTTLMin,
		},
		Telegram: server.TelegramConfig{
			APIID:       id,
			APIHash:     *apiHash,
			Phone:       *phone,
			Password:    password,
			SessionPath: *sessionPath,
			LoginOnly:   *loginOnly,
			CodePrompt: func(ctx context.Context) (string, error) {
				fmt.Print("Enter Telegram auth code: ")
				reader := bufio.NewReader(os.Stdin)
				code, err := reader.ReadString('\n')
				if err != nil {
					return "", err
				}
				return strings.TrimSpace(code), nil
			},
		},
	}

	srv, err := server.New(cfg)
	if err != nil {
		log.Fatalf("Create server: %v", err)
	}

	ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer cancel()

	log.Printf("Starting thefeed server %s on %s for domain %s", version.Version, cfg.ListenAddr, cfg.Domain)
	if err := srv.Run(ctx); err != nil && ctx.Err() == nil {
		log.Fatalf("Server error: %v", err)
	}
	log.Println("Server stopped")
}
