package output

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"reflect"
	"strings"
	"text/tabwriter"

	"gopkg.in/yaml.v3"
)

func Print(envelope *Envelope, format string) error {
	if format == "" {
		format = "json"
	}
	return PrintTo(os.Stdout, envelope, format)
}

func PrintTo(w io.Writer, envelope *Envelope, format string) error {
	switch format {
	case "json":
		return printJSON(w, envelope)
	case "yaml":
		return printYAML(w, envelope)
	case "table":
		return printTable(w, envelope)
	default:
		return printJSON(w, envelope)
	}
}

func printJSON(w io.Writer, envelope *Envelope) error {
	data, err := json.MarshalIndent(envelope, "", "  ")
	if err != nil {
		return err
	}
	_, err = fmt.Fprintln(w, string(data))
	return err
}

func printYAML(w io.Writer, envelope *Envelope) error {
	data, err := yaml.Marshal(envelope)
	if err != nil {
		return err
	}
	_, err = fmt.Fprint(w, string(data))
	return err
}

func printTable(w io.Writer, envelope *Envelope) error {
	if !envelope.OK {
		if envelope.Error != nil {
			fmt.Fprintf(w, "Error: %s\n", envelope.Error.Message)
			if envelope.Error.Suggestion != "" {
				fmt.Fprintf(w, "Suggestion: %s\n", envelope.Error.Suggestion)
			}
		}
		return nil
	}

	if envelope.Data == nil {
		fmt.Fprintln(w, "No data")
		return nil
	}

	// Try to render as table if data is a slice of maps
	switch data := envelope.Data.(type) {
	case []interface{}:
		return printSliceTable(w, data)
	case map[string]interface{}:
		// GitLink API 经常返回 {"count":N, "items":[...]} 这种"包装列表"
		// 优先解包内层 slice of maps，按列表渲染
		if unwrapped := unwrapSingleListField(data); unwrapped != nil {
			return printSliceTable(w, unwrapped)
		}
		// For maps with nested structures, prefer JSON
		if hasComplexValues(data) {
			return printJSON(w, envelope)
		}
		return printMapTable(w, data)
	default:
		// Fallback to JSON
		return printJSON(w, envelope)
	}
}

// unwrapSingleListField 检测 map 是否为"包装列表"结构：
// 至少包含一个 []interface{}（元素为 map，或为空数组）字段。
// 若是，返回该 slice（用于按列表渲染表格）；否则返回 nil。
//
// 优先选择已知列表字段名（projects/webhooks/issues 等），
// 若 map 中只有一个 slice of maps 字段，也直接使用。
func unwrapSingleListField(m map[string]interface{}) []interface{} {
	knownListFields := []string{
		"projects", "webhooks", "issues", "users", "pull_requests",
		"builds", "releases", "branches", "teams", "members",
		"orgs", "items", "records", "results", "wikis", "search",
	}

	// 1. 优先选择已知字段名（即使数组为空，也接受 — 空数组也是合法列表）
	for _, name := range knownListFields {
		if s, ok := m[name].([]interface{}); ok {
			if isSliceOfMaps(s) {
				return s
			}
		}
	}

	// 2. 兜底：检测是否只有一个 slice of maps 字段
	var listField string
	var listValue []interface{}
	for k, v := range m {
		s, ok := v.([]interface{})
		if !ok {
			continue
		}
		if !isSliceOfMaps(s) {
			continue
		}
		if listField != "" {
			// 多个列表字段，无法自动解包
			return nil
		}
		listField = k
		listValue = s
	}
	return listValue
}

// isSliceOfMaps 判断 slice 是否为 map 元素的列表
// 空 slice 也算合法列表（用于在 printSliceTable 中触发 "No results" 输出）
func isSliceOfMaps(s []interface{}) bool {
	if len(s) == 0 {
		return true // 空数组视为列表，printSliceTable 会输出 "No results"
	}
	_, ok := s[0].(map[string]interface{})
	return ok
}

func hasComplexValues(m map[string]interface{}) bool {
	for _, v := range m {
		switch v.(type) {
		case map[string]interface{}, []interface{}:
			return true
		}
	}
	return false
}

func printSliceTable(w io.Writer, items []interface{}) error {
	if len(items) == 0 {
		fmt.Fprintln(w, "No results")
		return nil
	}

	// Collect headers from first item
	first, ok := items[0].(map[string]interface{})
	if !ok {
		data, _ := json.MarshalIndent(items, "", "  ")
		fmt.Fprintln(w, string(data))
		return nil
	}

	headers := collectKeys(first)
	tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)

	// Print headers
	fmt.Fprintln(tw, strings.Join(headers, "\t"))
	dashes := make([]string, len(headers))
	for i, h := range headers {
		dashes[i] = strings.Repeat("-", len(h))
	}
	fmt.Fprintln(tw, strings.Join(dashes, "\t"))

	// Print rows
	for _, item := range items {
		m, ok := item.(map[string]interface{})
		if !ok {
			continue
		}
		vals := make([]string, len(headers))
		for i, h := range headers {
			vals[i] = formatValue(m[h])
		}
		fmt.Fprintln(tw, strings.Join(vals, "\t"))
	}
	return tw.Flush()
}

func printMapTable(w io.Writer, m map[string]interface{}) error {
	tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)
	fmt.Fprintln(tw, "KEY\tVALUE")
	fmt.Fprintln(tw, "---\t-----")
	for k, v := range m {
		fmt.Fprintf(tw, "%s\t%s\n", k, formatValue(v))
	}
	return tw.Flush()
}

func collectKeys(m map[string]interface{}) []string {
	keys := make([]string, 0, len(m))
	// Prefer common keys first
	priority := []string{"id", "name", "login", "title", "status", "state", "created_at", "updated_at"}
	seen := map[string]bool{}
	for _, k := range priority {
		if _, ok := m[k]; ok {
			keys = append(keys, k)
			seen[k] = true
		}
	}
	for k := range m {
		if !seen[k] {
			keys = append(keys, k)
		}
	}
	return keys
}

func formatValue(v interface{}) string {
	if v == nil {
		return ""
	}
	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Map, reflect.Slice:
		data, _ := json.Marshal(v)
		s := string(data)
		if len(s) > 60 {
			return s[:57] + "..."
		}
		return s
	default:
		return fmt.Sprintf("%v", v)
	}
}
