package main

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"pinscrape-allgo/internal/config"
	"pinscrape-allgo/internal/csvgen"
	"pinscrape-allgo/internal/pipeline"
	"pinscrape-allgo/internal/run"
)

func main() {
	log.SetFlags(log.Ltime)
	root := flagRoot()
	cfg, err := config.Load(root)
	if err != nil {
		fatalPause(root, err)
	}

	args := commandArgs()
	if len(args) == 0 {
		runInteractive(cfg)
		return
	}

	ctx, cancel := run.Context()
	defer cancel()
	defer func() {
		if r := recover(); r != nil {
			fmt.Fprintf(os.Stderr, "panic: %v\n", r)
			os.Exit(1)
		}
	}()
	if err := execute(ctx, cfg, args); err != nil {
		log.Fatalf("Error: %v", err)
	}
}

func flagRoot() string {
	root := ""
	for i, a := range os.Args[1:] {
		if a == "--root" && i+2 < len(os.Args) {
			root = os.Args[i+2]
		}
	}
	if root != "" {
		if abs, err := filepath.Abs(root); err == nil {
			return abs
		}
		return root
	}
	return config.FindRoot()
}

// commandArgs returns os.Args without the --root flag, so "pinscrape --root X
// snippet" reaches execute as just ["snippet"].
func commandArgs() []string {
	var out []string
	skip := false
	for _, a := range os.Args[1:] {
		if skip {
			skip = false
			continue
		}
		if a == "--root" {
			skip = true
			continue
		}
		out = append(out, a)
	}
	return out
}

func fatalPause(root string, err error) {
	fmt.Printf("Cannot load config.json from %s\n  -> %v\n\n", root, err)
	fmt.Println("Put config.json in the project root (folder above all-go), then run again.")
	pressAnyKey(nil, "Press any key to close...")
	os.Exit(1)
}

type menuItem struct {
	key   string
	name  string
	args  []string
	group string
}

func menu() []menuItem {
	return []menuItem{
		{key: "1", name: "Convert keywords to database", args: []string{"convert"}},
		{key: "2", name: "Scrape images + description snippet", args: []string{"scrape-all"}},
		{key: "3", name: "Generate AI titles", args: []string{"ai", "title"}},
		{key: "4", name: "Generate AI articles", args: []string{"ai", "article"}},
		{key: "5", name: "Delete AI titles", args: []string{"ai", "delete-title"}},
		{key: "6", name: "Delete AI articles", args: []string{"ai", "delete-article"}},
		{key: "7", name: "Full Workflow AI titles only (images + description snippet + AI titles)", args: []string{"workflow-titles"}},
		{key: "8", name: "Full Workflow All (images + description snippet + AI titles + AI articles)", args: []string{"workflow-all"}},
		{key: "9", name: "Create bucket & upload", args: []string{"deploy"}},
		{key: "10", name: "Submit indexnow", args: []string{"submit"}},
		{key: "11", name: "Export Only", args: []string{"export"}, group: "--- Export ---"},
		{key: "12", name: "Export + create bucket & upload + submit", args: []string{"export-all"}},
		{key: "13", name: "FULL AUTO SNIPPET : (convert + scrape + description snippet + export + deploy + submit)", args: []string{"full-auto-snippet"}},
		{key: "14", name: "FULL AUTO AI : (convert + scrape + description snippet + AI titles & articles + export + deploy + submit)", args: []string{"full-auto"}},
		{key: "15", name: "Exit", args: nil},
	}
}

func runInteractive(cfg *config.Config) {
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Println()
		fmt.Println("=============================")
		fmt.Println("   PinScrape CLI (Go)")
		fmt.Println("=============================")
		fmt.Println("-----------------------------")
		for _, m := range menu() {
			if m.group != "" {
				fmt.Println(m.group)
			}
			fmt.Printf("%3s. %s\n", m.key, m.name)
		}
		fmt.Println("=============================")
		fmt.Print("Select menu: ")

		line, _ := reader.ReadString('\n')
		choice := strings.TrimSpace(line)

		var picked *menuItem
		for i := range menu() {
			if menu()[i].key == choice {
				picked = &menu()[i]
				break
			}
		}
		if picked == nil {
			fmt.Println("Invalid choice.")
			continue
		}
		if picked.args == nil {
			fmt.Println("Bye.")
			return
		}

		ctx, cancel := context.WithCancel(context.Background())
		done := make(chan error, 1)
		go func() {
			defer func() {
				if r := recover(); r != nil {
					done <- fmt.Errorf("panic: %v", r)
				}
			}()
			done <- execute(ctx, cfg, picked.args)
		}()
		err := <-done
		cancel()

		if err != nil {
			fmt.Printf("\nError: %v\n", err)
		}
		pressAnyKey(reader, "\nPress any key to back to menu...")
	}
}

func execute(ctx context.Context, cfg *config.Config, args []string) error {
	cmd := args[0]
	sub := ""
	if len(args) > 1 {
		sub = args[1]
	}

	switch cmd {
	case "convert":
		return pipeline.Convert(cfg)
	case "scrape-all":
		env := run.NewEnv(cfg)
		return run.ScrapeAll(ctx, env)
	case "ai":
		switch sub {
		case "title", "article":
			env := run.NewEnv(cfg)
			return run.GenerateAI(ctx, env, sub)
		case "delete-title":
			return run.DeleteAITitles(cfg)
		case "delete-article":
			return run.DeleteAIArticles(cfg)
		default:
			return fmt.Errorf("usage: pinscrape ai <title|article|delete-title|delete-article>")
		}
	case "workflow-titles":
		env := run.NewEnv(cfg)
		return run.WorkflowTitles(ctx, env)
	case "workflow-all":
		env := run.NewEnv(cfg)
		return run.WorkflowAll(ctx, env)
	case "deploy":
		return run.Deploy(cfg)
	case "submit":
		return run.Submit(cfg)
	case "export":
		opts, ok := run.PromptExportOptions(cfg)
		if !ok {
			return nil
		}
		env := run.NewEnv(cfg)
		return run.RunExport(env, opts)
	case "export-all":
		opts, ok := run.PromptExportOptions(cfg)
		if !ok {
			return nil
		}
		env := run.NewEnv(cfg)
		return run.ExportAll(env, opts)
	case "full-auto-snippet":
		env := run.NewEnv(cfg)
		return run.FullAutoSnippet(ctx, env)
	case "full-auto":
		env := run.NewEnv(cfg)
		return run.FullAutoAI(ctx, env)
	case "status":
		return pipeline.UpdateStatus(cfg)
	case "report":
		return run.Report(cfg)
	case "csv":
		split := flagOrInt(args, "--split-size", cfg.SplitSize)
		_, err := csvgen.Generate(cfg, split)
		return err
	default:
		return fmt.Errorf("unknown command %q", cmd)
	}
}

func flagOrInt(args []string, name string, def int) int {
	for i, a := range args {
		if a == name && i+1 < len(args) {
			if v, err := strconv.Atoi(args[i+1]); err == nil {
				return v
			}
		}
	}
	return def
}
