package cli

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"osaet/backend/internal/ai"
)

func runPosts(root string, args []string) error {
	if len(args) == 0 {
		return errors.New("missing posts subcommand")
	}
	switch args[0] {
	case "new":
		return runPostsNew(root, args[1:])
	case "slug":
		return runPostsSlug(root, args[1:])
	case "list":
		return runPostsList(root, args[1:])
	case "show":
		return runPostsShow(root, args[1:])
	case "publish":
		return runPostsStatus(root, args[1:], "published")
	case "unpublish":
		return runPostsStatus(root, args[1:], "draft")
	case "delete":
		return runPostsDelete(root, args[1:])
	case "edit":
		return runPostsEdit(root, args[1:])
	case "import":
		return runPostsImport(root, args[1:])
	case "export":
		return runPostsExport(root, args[1:])
	case "diff":
		return runPostsDiff(root, args[1:])
	case "sync":
		return runPostsSync(root, args[1:])
	default:
		return fmt.Errorf("unknown posts subcommand %q", args[0])
	}
}

func runTags(root string, args []string) error {
	if len(args) == 0 {
		return errors.New("missing tags subcommand")
	}
	switch args[0] {
	case "list":
		return runTagsList(root, args[1:])
	default:
		return fmt.Errorf("unknown tags subcommand %q", args[0])
	}
}

func runTagsList(root string, args []string) error {
	fs := flag.NewFlagSet("tags list", flag.ContinueOnError)
	fs.SetOutput(os.Stderr)
	all := fs.Bool("all", false, "include draft posts")
	if err := fs.Parse(args); err != nil {
		return err
	}

	posts, err := loadPosts(root)
	if err != nil {
		return err
	}

	counts := map[string]int{}
	for _, post := range posts {
		if !*all && post.Frontmatter.Status != "published" {
			continue
		}
		for _, tag := range post.Frontmatter.Tags {
			tag = strings.TrimSpace(tag)
			if tag != "" {
				counts[tag]++
			}
		}
	}

	for _, tag := range sortedKeys(counts) {
		fmt.Printf("%-24s %d\n", tag, counts[tag])
	}
	return nil
}

func runPostsNew(root string, args []string) error {
	fs := flag.NewFlagSet("posts new", flag.ContinueOnError)
	fs.SetOutput(os.Stderr)

	title := fs.String("title", "", "post title")
	slug := fs.String("slug", "", "post slug")
	status := fs.String("status", "draft", "post status: draft or published")
	summary := fs.String("summary", "", "post summary")
	body := fs.String("body", "", "initial markdown body")
	aiSlug := fs.Bool("ai-slug", true, "generate slug with DeepSeek when --slug is empty")
	noAISlug := fs.Bool("no-ai-slug", false, "disable AI slug generation")
	tags := stringListFlag{}
	fs.Var(&tags, "tag", "post tag; may be repeated")
	if err := fs.Parse(args); err != nil {
		return err
	}
	if strings.TrimSpace(*title) == "" {
		return errors.New("missing required --title")
	}

	cleanSlug := ""
	slugSource := "manual"
	if strings.TrimSpace(*slug) != "" {
		cleanSlug = sanitizeSlug(*slug)
	} else if *aiSlug && !*noAISlug {
		generatedSlug, err := generateDeepSeekSlug(context.Background(), root, *title, *summary)
		if err == nil {
			cleanSlug = generatedSlug
			slugSource = "ai"
		} else {
			fmt.Fprintf(os.Stderr, "warning: AI slug generation failed, using local fallback: %v\n", err)
		}
	}
	if cleanSlug == "" {
		cleanSlug = fallbackSlug(*title)
	}
	if cleanSlug == "" {
		return errors.New("could not derive slug; pass --slug")
	}
	cleanSlug, err := uniqueSlug(root, cleanSlug)
	if err != nil {
		return err
	}

	if *status != "draft" && *status != "published" {
		return errors.New("--status must be draft or published")
	}

	postsDir := filepath.Join(root, defaultPostsDir)
	if err := os.MkdirAll(postsDir, 0o755); err != nil {
		return err
	}

	path, err := uniquePostPath(postsDir, cleanSlug)
	if err != nil {
		return err
	}

	now := time.Now().Format(time.RFC3339)
	publishedAt := "null"
	if *status == "published" {
		publishedAt = fmt.Sprintf("%q", now)
	}

	content := strings.TrimSpace(*body)
	if content == "" {
		content = "Write your post here."
	}

	post := fmt.Sprintf(`---
id: "%s"
slug: "%s"
title: "%s"
summary: "%s"
status: "%s"
tags: %s
cover: ""
version: 1
slug_source: "%s"
slug_locked: false
published_at: %s
created_at: "%s"
updated_at: "%s"
---

%s
`, randomID(), cleanSlug, escapeYAML(*title), escapeYAML(*summary), *status, formatYAMLStringList(tags.Values()), slugSource, publishedAt, now, now, content)

	if err := os.WriteFile(path, []byte(post), 0o644); err != nil {
		return err
	}

	rel, err := filepath.Rel(root, path)
	if err != nil {
		rel = path
	}
	fmt.Println(rel)
	return nil
}

func runPostsSlug(root string, args []string) error {
	fs := flag.NewFlagSet("posts slug", flag.ContinueOnError)
	fs.SetOutput(os.Stderr)
	title := fs.String("title", "", "post title")
	summary := fs.String("summary", "", "optional post summary")
	if err := fs.Parse(args); err != nil {
		return err
	}
	if strings.TrimSpace(*title) == "" {
		return errors.New("missing required --title")
	}

	slug, err := generateDeepSeekSlug(context.Background(), root, *title, *summary)
	if err != nil {
		return err
	}
	slug, err = uniqueSlug(root, slug)
	if err != nil {
		return err
	}
	fmt.Println(slug)
	return nil
}

func runPostsList(root string, args []string) error {
	fs := flag.NewFlagSet("posts list", flag.ContinueOnError)
	fs.SetOutput(os.Stderr)
	status := fs.String("status", "", "filter by status")
	if err := fs.Parse(args); err != nil {
		return err
	}

	posts, err := loadPosts(root)
	if err != nil {
		return err
	}

	for _, post := range posts {
		if *status != "" && post.Frontmatter.Status != *status {
			continue
		}
		fmt.Printf("%-12s %-32s %-24s %s\n", post.Frontmatter.Status, post.Frontmatter.Slug, strings.Join(post.Frontmatter.Tags, ","), post.Frontmatter.Title)
	}
	return nil
}

func runPostsShow(root string, args []string) error {
	if len(args) != 1 {
		return errors.New("usage: osaetctl posts show <slug>")
	}

	post, err := loadPostBySlug(root, args[0])
	if err != nil {
		return err
	}

	fmt.Printf("title: %s\n", post.Frontmatter.Title)
	fmt.Printf("slug: %s\n", post.Frontmatter.Slug)
	fmt.Printf("status: %s\n", post.Frontmatter.Status)
	fmt.Printf("summary: %s\n", post.Frontmatter.Summary)
	fmt.Printf("tags: %s\n", strings.Join(post.Frontmatter.Tags, ", "))
	fmt.Printf("path: %s\n", mustRel(root, post.Path))
	fmt.Println()
	fmt.Println(post.Body)
	return nil
}

func runPostsStatus(root string, args []string, status string) error {
	if len(args) != 1 {
		return fmt.Errorf("usage: osaetctl posts %s <slug>", statusCommand(status))
	}

	post, err := loadPostBySlug(root, args[0])
	if err != nil {
		return err
	}

	now := time.Now().Format(time.RFC3339)
	post.Frontmatter.Status = status
	post.Frontmatter.UpdatedAt = now
	post.Frontmatter.Version++
	if status == "published" && post.Frontmatter.PublishedAt == nil {
		post.Frontmatter.PublishedAt = &now
	}
	if status == "draft" {
		post.Frontmatter.PublishedAt = nil
	}

	if err := writePostFile(post); err != nil {
		return err
	}
	fmt.Printf("%s -> %s\n", post.Frontmatter.Slug, status)
	return nil
}

func runPostsDelete(root string, args []string) error {
	if len(args) != 1 {
		return errors.New("usage: osaetctl posts delete <slug>")
	}

	post, err := loadPostBySlug(root, args[0])
	if err != nil {
		return err
	}

	trashDir := filepath.Join(root, defaultTrashDir)
	if err := os.MkdirAll(trashDir, 0o755); err != nil {
		return err
	}

	target := uniquePath(filepath.Join(trashDir, filepath.Base(post.Path)))
	if err := os.Rename(post.Path, target); err != nil {
		return err
	}

	fmt.Printf("%s -> %s\n", mustRel(root, post.Path), mustRel(root, target))
	return nil
}

func runPostsEdit(root string, args []string) error {
	if len(args) != 1 {
		return errors.New("usage: osaetctl posts edit <slug>")
	}

	editor := strings.TrimSpace(os.Getenv("EDITOR"))
	if editor == "" {
		return errors.New("EDITOR is not set")
	}

	post, err := loadPostBySlug(root, args[0])
	if err != nil {
		return err
	}

	cmd := exec.Command(editor, post.Path)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

func generateDeepSeekSlug(ctx context.Context, root string, title string, summary string) (string, error) {
	config, err := loadLocalConfig(root)
	if err != nil {
		return "", err
	}

	apiKeyEnv := strings.TrimSpace(config.DeepSeek.APIKeyEnv)
	if apiKeyEnv == "" {
		apiKeyEnv = "DEEPSEEK_API_KEY"
	}

	apiKey := strings.TrimSpace(os.Getenv(apiKeyEnv))
	if apiKey == "" {
		apiKey = strings.TrimSpace(config.DeepSeek.APIKey)
	}
	if apiKey == "" {
		return "", fmt.Errorf("%s is not set and config/local.yaml deepseek.api_key is empty", apiKeyEnv)
	}

	baseURL := strings.TrimRight(strings.TrimSpace(os.Getenv("DEEPSEEK_BASE_URL")), "/")
	if baseURL == "" {
		baseURL = strings.TrimRight(strings.TrimSpace(config.DeepSeek.BaseURL), "/")
	}
	model := strings.TrimSpace(os.Getenv("DEEPSEEK_MODEL"))
	if model == "" {
		model = strings.TrimSpace(config.DeepSeek.Model)
	}
	return ai.GenerateSlug(ctx, ai.Config{
		APIKey:  apiKey,
		BaseURL: baseURL,
		Model:   model,
	}, title, summary)
}
