package main

import (
	"bufio"
	"embed"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	"github.com/voocel/ainovel-cli/app"
	"github.com/voocel/ainovel-cli/tools"
	"github.com/voocel/ainovel-cli/tui"
)

//go:embed prompts/*.md
var promptsFS embed.FS

//go:embed references
var referencesFS embed.FS

//go:embed styles/*.md
var stylesFS embed.FS

func main() {
	// 处理 start 子命令
	if len(os.Args) > 1 && os.Args[1] == "start" {
		handleStartCommand()
		return
	}

	style := envOr("NOVEL_STYLE", "default")
	refs := loadReferences(style)
	prompts := loadPrompts()
	styles := loadStyles()
	cfg := buildConfig(style)

	prompt := parsePrompt()
	if prompt != "" {
		// CLI 模式：有命令行参数，直接运行
		cfg.Prompt = prompt
		if err := app.Run(cfg, refs, prompts, styles); err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		return
	}

	// TUI 模式：无命令行参数，启动交互界面
	if err := tui.Run(cfg, refs, prompts, styles); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}

func buildConfig(style string) app.Config {
	provider := envOr("LLM_PROVIDER", "openrouter")
	apiKey := os.Getenv("Z_OPENAI_API_KEY")
	baseURL := os.Getenv("Z_OPENAI_BASE_URL")
	switch provider {
	case "anthropic":
		apiKey = envOr("ANTHROPIC_API_KEY", apiKey)
		baseURL = envOr("ANTHROPIC_BASE_URL", baseURL)
	case "gemini":
		apiKey = envOr("GEMINI_API_KEY", apiKey)
		baseURL = envOr("GEMINI_BASE_URL", baseURL)
	case "openrouter":
		apiKey = envOr("OPENROUTER_API_KEY", apiKey)
		baseURL = envOr("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1")
	}

	cfg := app.Config{
		NovelName: envOr("NOVEL_NAME", ""),
		Provider:  provider,
		APIKey:    apiKey,
		BaseURL:   baseURL,
		ModelName: envOr("LLM_MODEL", "stepfun/step-3.5-flash:free"),
		Style:     style,
	}
	return cfg
}

func parsePrompt() string {
	if len(os.Args) < 2 {
		return ""
	}
	if os.Args[1] == "-help" || os.Args[1] == "--help" || os.Args[1] == "-h" {
		printHelp()
		os.Exit(0)
	}
	return strings.Join(os.Args[1:], " ")
}

func printHelp() {
	fmt.Println(`ainovel-cli - AI 小说生成工具

用法:
  ainovel-cli start              交互式开始：选择小说 → 选择风格 → 启动 TUI
  ainovel-cli [prompt]          CLI 模式：直接生成小说
  ainovel-cli                   TUI 模式：启动交互界面

环境变量:
  NOVEL_NAME        小说名称（默认：novel）
  NOVEL_STYLE       小说风格（默认：default）
                    可选值：
                      default   通用风格，叙事张弛有度，五感描写，对话自然
                      fantasy   奇幻冒险，世界观自然展开，魔法体系有代价感
                      romance   言情，情感递进有节奏，关系张力与内心描写并重
                      suspense  悬疑推理，多线叙事，信息差悬念，线索管理严谨
  LLM_PROVIDER      LLM 提供商：openrouter|anthropic|gemini（默认：openrouter）
  LLM_MODEL         模型名称（默认：stepfun/step-3.5-flash:free）

  Z_OPENAI_API_KEY  API 密钥
  Z_OPENAI_BASE_URL API 地址

  ANTHROPIC_API_KEY Anthropic API 密钥
  ANTHROPIC_BASE_URL Anthropic API 地址

  GEMINI_API_KEY    Gemini API 密钥
  GEMINI_BASE_URL   Gemini API 地址

  OPENROUTER_API_KEY OpenRouter API 密钥
  OPENROUTER_BASE_URL OpenRouter API 地址

示例:
  ainovel-cli "写一部科幻小说，主角是时间旅行者"
  NOVEL_NAME=时空之旅 ainovel-cli "写一部科幻小说"
  ainovel-cli  # 启动 TUI 交互模式

tmux 使用（推荐后台运行）:
  tmux new -s novel          # 新建名为 novel 的会话
  ainovel-cli "写一部科幻小说" # 在会话中运行，关闭终端不中断
  Ctrl+b d                   # 脱离会话（程序继续跑）
  tmux attach -t novel       # 随时回来查看进度
  tmux ls                    # 列出所有会话
  tmux kill-session -t novel # 结束会话`)
}

func loadReferences(style string) tools.References {
	refs := tools.References{
		ChapterGuide:      mustRead(referencesFS, "references/chapter-guide.md"),
		HookTechniques:    mustRead(referencesFS, "references/hook-techniques.md"),
		QualityChecklist:  mustRead(referencesFS, "references/quality-checklist.md"),
		OutlineTemplate:   mustRead(referencesFS, "references/outline-template.md"),
		CharacterTemplate: mustRead(referencesFS, "references/character-template.md"),
		ChapterTemplate:   mustRead(referencesFS, "references/chapter-template.md"),
		Consistency:       mustRead(referencesFS, "references/consistency.md"),
		ContentExpansion:  mustRead(referencesFS, "references/content-expansion.md"),
		DialogueWriting:   mustRead(referencesFS, "references/dialogue-writing.md"),
		LongformPlanning:  mustRead(referencesFS, "references/longform-planning.md"),
		Differentiation:   mustRead(referencesFS, "references/differentiation.md"),
	}
	if style != "" && style != "default" {
		path := "references/" + style + "/style-references.md"
		if data, err := referencesFS.ReadFile(path); err == nil {
			refs.StyleReference = string(data)
		}
	}
	return refs
}

func loadPrompts() app.Prompts {
	return app.Prompts{
		Coordinator:    mustRead(promptsFS, "prompts/coordinator.md"),
		ArchitectShort: mustRead(promptsFS, "prompts/architect-short.md"),
		ArchitectMid:   mustRead(promptsFS, "prompts/architect-mid.md"),
		ArchitectLong:  mustRead(promptsFS, "prompts/architect-long.md"),
		Writer:         mustRead(promptsFS, "prompts/writer.md"),
		Editor:         mustRead(promptsFS, "prompts/editor.md"),
	}
}

func mustRead(fs embed.FS, path string) string {
	data, err := fs.ReadFile(path)
	if err != nil {
		panic(fmt.Sprintf("embed read %s: %v", path, err))
	}
	return string(data)
}

func loadStyles() map[string]string {
	styles := make(map[string]string)
	entries, err := stylesFS.ReadDir("styles")
	if err != nil {
		return styles
	}
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		name := strings.TrimSuffix(e.Name(), ".md")
		data, err := stylesFS.ReadFile("styles/" + e.Name())
		if err != nil {
			continue
		}
		styles[name] = string(data)
	}
	return styles
}

func envOr(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

// handleStartCommand 处理 start 子命令：交互式选择小说和风格
func handleStartCommand() {
	// Step 1: 选择小说
	novelName := selectNovel()
	if novelName == "" {
		fmt.Println("已取消")
		os.Exit(0)
	}

	// Step 2: 选择风格
	style := selectStyle()
	if style == "" {
		fmt.Println("已取消")
		os.Exit(0)
	}

	// 设置环境变量并启动 TUI
	os.Setenv("NOVEL_NAME", novelName)
	os.Setenv("NOVEL_STYLE", style)

	refs := loadReferences(style)
	prompts := loadPrompts()
	styles := loadStyles()
	cfg := buildConfig(style)
	cfg.NovelName = novelName

	if err := tui.Run(cfg, refs, prompts, styles); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}

// selectNovel 显示小说选择菜单
func selectNovel() string {
	options := []string{"📝 创建新小说"}

	// 扫描 output 目录下现有小说
	outputDir := "output"
	entries, err := ioutil.ReadDir(outputDir)
	novels := []string{}
	if err == nil {
		for _, e := range entries {
			if e.IsDir() && e.Name() != "." && e.Name() != ".." {
				novels = append(novels, e.Name())
				options = append(options, "📖 "+e.Name())
			}
		}
	}

	if len(options) == 1 {
		// 只有创建选项，直接输入新名称
		return promptNovelName()
	}

	// 显示菜单
	fmt.Println("\n=== 选择小说或创建新的 ===")
	for i, opt := range options {
		fmt.Printf("%d. %s\n", i+1, opt)
	}

	choice := promptChoice(len(options))
	if choice < 0 {
		return ""
	}

	if choice == 0 {
		// 创建新小说
		return promptNovelName()
	}

	// 返回现有小说名称
	return novels[choice-1]
}

// selectStyle 显示风格选择菜单
func selectStyle() string {
	styles := []struct {
		name string
		desc string
	}{
		{"default", "通用风格，叙事张弛有度，五感描写，对话自然"},
		{"fantasy", "奇幻冒险，世界观自然展开，魔法体系有代价感"},
		{"romance", "言情，情感递进有节奏，关系张力与内心描写并重"},
		{"suspense", "悬疑推理，多线叙事，信息差悬念，线索管理严谨"},
	}

	fmt.Println("\n=== 选择小说风格 ===")
	for i, s := range styles {
		fmt.Printf("%d. %-10s  %s\n", i+1, s.name, s.desc)
	}

	choice := promptChoice(len(styles))
	if choice < 0 {
		// EOF 或错误，使用默认风格
		fmt.Println("使用默认风格...")
		return "default"
	}
	return styles[choice].name
}

// promptNovelName 提示输入新小说名称
func promptNovelName() string {
	reader := bufio.NewReader(os.Stdin)
	fmt.Print("\n请输入小说名称 (默认: novel): ")
	name, err := reader.ReadString('\n')
	if err != nil {
		return "novel"
	}
	name = strings.TrimSpace(name)
	if name == "" {
		return "novel"
	}
	return name
}

// promptChoice 提示用户选择菜单项
func promptChoice(maxChoice int) int {
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("\n请选择 (输入数字): ")
		input, err := reader.ReadString('\n')
		if err != nil {
			// EOF 或其他错误，返回 -1 表示取消
			return -1
		}
		input = strings.TrimSpace(input)
		if input == "" {
			continue
		}

		choice, err := strconv.Atoi(input)
		if err != nil || choice < 1 || choice > maxChoice {
			fmt.Printf("❌ 请输入 1 到 %d 之间的数字\n", maxChoice)
			continue
		}
		return choice - 1 // 转换为 0-based 索引
	}
}
