package cmd

import (
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"forgejo.zerova.net/public/fj/internal/api"
	"forgejo.zerova.net/public/fj/internal/config"
	"forgejo.zerova.net/public/fj/internal/text"
	"github.com/spf13/cobra"
)

// Wiki API response types

type wikiPageMeta struct {
	Title      string      `json:"title"`
	HTMLURL    string      `json:"html_url"`
	SubURL     string      `json:"sub_url"`
	LastCommit *wikiCommit `json:"last_commit"`
}

type wikiCommit struct {
	ID        string    `json:"id"`
	Author    *wikiUser `json:"author"`
	Committer *wikiUser `json:"committer"`
	Message   string    `json:"message"`
}

type wikiUser struct {
	Name  string `json:"name"`
	Email string `json:"email"`
	Date  string `json:"date"`
}

type wikiPage struct {
	Title         string      `json:"title"`
	HTMLURL       string      `json:"html_url"`
	SubURL        string      `json:"sub_url"`
	ContentBase64 string      `json:"content_base64"`
	LastCommit    *wikiCommit `json:"last_commit"`
	// Decoded content for JSON output
	Content string `json:"content,omitempty"`
}

type wikiCreateRequest struct {
	Title         string `json:"title"`
	ContentBase64 string `json:"content_base64"`
}

var wikiCmd = &cobra.Command{
	Use:   "wiki",
	Short: "Manage repository wiki pages",
	Long:  "View and manage wiki pages for a repository.",
}

var wikiListCmd = &cobra.Command{
	Use:   "list",
	Short: "List wiki pages",
	Long:  "List all wiki pages for a repository.",
	Example: `  # List wiki pages for the current repo
  fj wiki list

  # List wiki pages for a specific repo
  fj wiki list -R owner/repo

  # Output as JSON
  fj wiki list --json`,
	RunE: runWikiList,
}

var wikiViewCmd = &cobra.Command{
	Use:   "view <title>",
	Short: "View a wiki page",
	Long:  "Display the content of a wiki page.",
	Example: `  # View a wiki page
  fj wiki view Home

  # Open in browser
  fj wiki view Home --web

  # View a wiki page as JSON (includes content)
  fj wiki view Home --json

  # View a wiki page from a specific repo
  fj wiki view "Getting-Started" -R owner/repo`,
	Args: cobra.ExactArgs(1),
	RunE: runWikiView,
}

var wikiCreateCmd = &cobra.Command{
	Use:   "create <title>",
	Short: "Create a wiki page",
	Long:  "Create a new wiki page in the repository.",
	Example: `  # Create a wiki page with inline content
  fj wiki create "Getting Started" -b "# Welcome\nThis is the getting started guide."

  # Create a wiki page from a file
  fj wiki create "Setup Guide" --body-file setup.md

  # Create a wiki page from stdin
  echo "# FAQ" | fj wiki create FAQ --body-file -

  # Output as JSON
  fj wiki create "New Page" -b "Content here" --json`,
	Args: cobra.ExactArgs(1),
	RunE: runWikiCreate,
}

var wikiEditCmd = &cobra.Command{
	Use:   "edit <title>",
	Short: "Edit a wiki page",
	Long:  "Edit an existing wiki page in the repository.",
	Example: `  # Edit a wiki page with new content
  fj wiki edit Home -b "# Updated Home\nNew content here."

  # Edit a wiki page from a file
  fj wiki edit "Setup Guide" --body-file updated-setup.md

  # Edit a wiki page from stdin
  cat new-content.md | fj wiki edit Home --body-file -

  # Output as JSON
  fj wiki edit Home -b "Updated content" --json`,
	Args: cobra.ExactArgs(1),
	RunE: runWikiEdit,
}

var wikiDeleteCmd = &cobra.Command{
	Use:   "delete <title>",
	Short: "Delete a wiki page",
	Long:  "Delete a wiki page from the repository.",
	Example: `  # Delete a wiki page
  fj wiki delete "Old Page"

  # Delete without confirmation
  fj wiki delete "Old Page" -y

  # Delete a wiki page from a specific repo
  fj wiki delete "Outdated Guide" -R owner/repo`,
	Args: cobra.ExactArgs(1),
	RunE: runWikiDelete,
}

func init() {
	rootCmd.AddCommand(wikiCmd)
	wikiCmd.AddCommand(wikiListCmd)
	wikiCmd.AddCommand(wikiViewCmd)
	wikiCmd.AddCommand(wikiCreateCmd)
	wikiCmd.AddCommand(wikiEditCmd)
	wikiCmd.AddCommand(wikiDeleteCmd)

	wikiListCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	addJSONFlags(wikiListCmd, "Output as JSON")

	wikiViewCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	addJSONFlags(wikiViewCmd, "Output as JSON")
	wikiViewCmd.Flags().BoolP("web", "w", false, "Open in web browser")

	wikiCreateCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	wikiCreateCmd.Flags().StringP("body", "b", "", "Wiki page content")
	wikiCreateCmd.Flags().String("body-file", "", "Read content from file (use \"-\" for stdin)")
	addJSONFlags(wikiCreateCmd, "Output created page as JSON")

	wikiEditCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	wikiEditCmd.Flags().StringP("body", "b", "", "Wiki page content")
	wikiEditCmd.Flags().String("body-file", "", "Read content from file (use \"-\" for stdin)")
	addJSONFlags(wikiEditCmd, "Output updated page as JSON")

	wikiDeleteCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	wikiDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
}

func newWikiClient(cmd *cobra.Command) (*api.Client, string, string, error) {
	repo, _ := cmd.Flags().GetString("repo")
	owner, name, err := parseRepo(repo)
	if err != nil {
		return nil, "", "", err
	}

	cfg, err := config.Load()
	if err != nil {
		return nil, "", "", err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return nil, "", "", err
	}

	return client, owner, name, nil
}

func runWikiList(cmd *cobra.Command, args []string) error {
	client, owner, name, err := newWikiClient(cmd)
	if err != nil {
		return err
	}

	path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/pages", url.PathEscape(owner), url.PathEscape(name))

	ios.StartSpinner("Fetching wiki pages...")
	var pages []wikiPageMeta
	if err := client.GetJSON(path, &pages); err != nil {
		ios.StopSpinner()
		return fmt.Errorf("failed to list wiki pages: %w", err)
	}
	ios.StopSpinner()

	if wantJSON(cmd) {
		return outputJSON(cmd, pages)
	}

	if len(pages) == 0 {
		fmt.Fprintln(ios.Out, "No wiki pages found")
		return nil
	}

	isTTY := ios.IsStdoutTTY()
	tp := ios.NewTablePrinter()
	tp.AddHeader("TITLE", "LAST UPDATED")
	for _, p := range pages {
		updated := ""
		if p.LastCommit != nil && p.LastCommit.Committer != nil && p.LastCommit.Committer.Date != "" {
			if t, err := time.Parse(time.RFC3339, p.LastCommit.Committer.Date); err == nil {
				updated = text.FormatDate(t, isTTY)
			} else {
				updated = p.LastCommit.Committer.Date
			}
		}
		tp.AddRow(p.Title, updated)
	}
	return tp.Render()
}

func runWikiView(cmd *cobra.Command, args []string) error {
	title := args[0]

	client, owner, name, err := newWikiClient(cmd)
	if err != nil {
		return err
	}

	path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s",
		url.PathEscape(owner), url.PathEscape(name), url.PathEscape(title))

	ios.StartSpinner("Fetching wiki page...")
	var page wikiPage
	if err := client.GetJSON(path, &page); err != nil {
		ios.StopSpinner()
		return fmt.Errorf("failed to get wiki page: %w", err)
	}
	ios.StopSpinner()

	content, err := base64.StdEncoding.DecodeString(page.ContentBase64)
	if err != nil {
		return fmt.Errorf("failed to decode wiki page content: %w", err)
	}

	if web, _ := cmd.Flags().GetBool("web"); web {
		if page.HTMLURL != "" {
			return ios.OpenInBrowser(page.HTMLURL)
		}
		return fmt.Errorf("wiki page has no HTML URL")
	}

	jsonFlag, _ := cmd.Flags().GetBool("json")
	if jsonFlag {
		page.Content = string(content)
		return writeJSON(page)
	}

	if err := ios.StartPager(); err != nil {
		fmt.Fprintf(ios.ErrOut, "warning: failed to start pager: %v\n", err)
	}
	defer ios.StopPager()

	fmt.Fprintf(ios.Out, "# %s\n\n", page.Title)
	fmt.Fprint(ios.Out, string(content))
	// Ensure trailing newline
	if len(content) > 0 && content[len(content)-1] != '\n' {
		fmt.Fprintln(ios.Out)
	}

	return nil
}

func runWikiCreate(cmd *cobra.Command, args []string) error {
	title := args[0]

	client, owner, name, err := newWikiClient(cmd)
	if err != nil {
		return err
	}

	body, err := readBody(cmd)
	if err != nil {
		return err
	}
	if body == "" {
		return fmt.Errorf("content is required (use --body or --body-file)")
	}

	path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new",
		url.PathEscape(owner), url.PathEscape(name))

	reqBody := wikiCreateRequest{
		Title:         title,
		ContentBase64: base64.StdEncoding.EncodeToString([]byte(body)),
	}

	ios.StartSpinner("Creating wiki page...")
	var page wikiPage
	if _, err := client.DoJSON(http.MethodPost, path, reqBody, &page); err != nil {
		ios.StopSpinner()
		return fmt.Errorf("failed to create wiki page: %w", err)
	}
	ios.StopSpinner()

	if wantJSON(cmd) {
		return outputJSON(cmd, page)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Wiki page created: %s\n", cs.SuccessIcon(), title)
	return nil
}

func runWikiEdit(cmd *cobra.Command, args []string) error {
	title := args[0]

	client, owner, name, err := newWikiClient(cmd)
	if err != nil {
		return err
	}

	body, err := readBody(cmd)
	if err != nil {
		return err
	}
	if body == "" {
		return fmt.Errorf("content is required (use --body or --body-file)")
	}

	path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s",
		url.PathEscape(owner), url.PathEscape(name), url.PathEscape(title))

	reqBody := wikiCreateRequest{
		Title:         title,
		ContentBase64: base64.StdEncoding.EncodeToString([]byte(body)),
	}

	ios.StartSpinner("Updating wiki page...")
	var page wikiPage
	if _, err := client.DoJSON(http.MethodPatch, path, reqBody, &page); err != nil {
		ios.StopSpinner()
		return fmt.Errorf("failed to update wiki page: %w", err)
	}
	ios.StopSpinner()

	if wantJSON(cmd) {
		return outputJSON(cmd, page)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Wiki page updated: %s\n", cs.SuccessIcon(), title)
	return nil
}

func runWikiDelete(cmd *cobra.Command, args []string) error {
	title := args[0]
	yes, _ := cmd.Flags().GetBool("yes")

	client, owner, name, err := newWikiClient(cmd)
	if err != nil {
		return err
	}

	if !yes {
		confirmed, err := ios.ConfirmAction(fmt.Sprintf("Delete wiki page %q?", title))
		if err != nil {
			return err
		}
		if !confirmed {
			fmt.Fprintln(ios.ErrOut, "Aborted")
			return nil
		}
	}

	path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s",
		url.PathEscape(owner), url.PathEscape(name), url.PathEscape(title))

	ios.StartSpinner("Deleting wiki page...")
	if _, err := client.DoJSON(http.MethodDelete, path, nil, nil); err != nil {
		ios.StopSpinner()
		return fmt.Errorf("failed to delete wiki page: %w", err)
	}
	ios.StopSpinner()

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Wiki page deleted: %s\n", cs.SuccessIcon(), title)
	return nil
}
