package view

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/atotto/clipboard"
	"github.com/charmbracelet/glamour"
	"github.com/fatih/color"
	"github.com/mgutz/ansi"
	"github.com/rivo/tview"

	"github.com/ankitpokhrel/jira-cli/internal/cmdutil"
	"github.com/ankitpokhrel/jira-cli/pkg/browser"
	"github.com/ankitpokhrel/jira-cli/pkg/tui"
)

const (
	wordWrap = 120
	tabWidth = 8
	helpText = `USAGE
	-----
	
	The layout contains 2 sections, viz: Sidebar and Contents screen.  
	
	You can use up and down arrow keys or 'j' and 'k' letters to navigate through the sidebar.
	Press 'w' or Tab to toggle focus between the sidebar and the contents screen.
	
	On contents screen:
	  - Use arrow keys or 'j', 'k', 'h', and 'l' letters to navigate through the issue list.
	  - Use 'g' and 'SHIFT+G' to quickly navigate to the top and bottom respectively.
	  - Press 'v' to view selected issue details.
	  - Press 'c' to copy issue URL to the system clipboard.
	  - Press 'CTRL+K' to copy issue key to the system clipboard.
	  - Hit ENTER to open the selected issue in a browser.
	
	Press 'q' / ESC / CTRL+C to quit.`

	tableHelpText = `[default]ACTIONS AVAILABLE IN THE TUI
----------------------------

* [yellow]← → ↑ ↓ / j, k, h, l[default] to navigate through the list
* [yellow]g[default] to quickly navigate to the top of the list
* [yellow]G[default] to quickly navigate to the bottom of the list
* [yellow]CTRL + f[default] to scroll through a page downwards
* [yellow]CTRL + b[default] to scroll through a page upwards
* [yellow]v[default] to view selected issue details
* [yellow]m[default] to move/transition selected issue
* [yellow]CTRL + r / F5[default] to refresh the issues list
* [yellow]ENTER[default] to open the selected issue in the browser
* [yellow]c[default] to copy issue URL to the system clipboard
* [yellow]CTRL + k[default] to copy issue key to the system clipboard
* [yellow]q / ESC / CTRL + c[default] to quit the app
* [yellow]?[default] to view this help page`
)

// ValidIssueColumns returns valid columns for issue list.
func ValidIssueColumns() []string {
	return []string{
		fieldType,
		fieldKey,
		fieldSummary,
		fieldStatus,
		fieldAssignee,
		fieldReporter,
		fieldPriority,
		fieldResolution,
		fieldCreated,
		fieldUpdated,
		fieldLabels,
	}
}

// ValidSprintColumns returns valid columns for sprint list.
func ValidSprintColumns() []string {
	return []string{
		fieldID,
		fieldName,
		fieldStartDate,
		fieldEndDate,
		fieldCompleteDate,
		fieldState,
	}
}

// MDRenderer constructs markdown renderer.
func MDRenderer() (*glamour.TermRenderer, error) {
	return glamour.NewTermRenderer(
		glamour.WithEnvironmentConfig(),
		glamour.WithWordWrap(wordWrap),
	)
}

func formatDateTime(dt, format, tz string) string {
	t, err := time.Parse(format, dt)
	if err != nil {
		return dt
	}
	if tz == "" {
		return t.Format("2006-01-02 15:04:05")
	}
	loc, err := time.LoadLocation(tz)
	if err != nil {
		return dt
	}
	return t.In(loc).Format("2006-01-02 15:04:05")
}

func prepareTitle(text string) string {
	text = strings.TrimSpace(text)
	return tview.Escape(text)
}

func issueKeyFromTuiData(r int, d any) string {
	var path string

	switch data := d.(type) {
	case tui.TableData:
		path = data.Get(r, data.GetIndex(fieldKey))
	case tui.PreviewData:
		path = data.Key
	}

	return path
}

func jiraURLFromTuiData(server string, r int, d any) string {
	return cmdutil.GenerateServerBrowseURL(server, issueKeyFromTuiData(r, d))
}

func navigate(server string) tui.SelectedFunc {
	return func(r, _ int, d any) {
		_ = browser.Browse(jiraURLFromTuiData(server, r, d))
	}
}

func copyURL(server string) tui.CopyFunc {
	return func(r, _ int, d any) {
		_ = clipboard.WriteAll(jiraURLFromTuiData(server, r, d))
	}
}

func copyKey() tui.CopyKeyFunc {
	return func(r, _ int, d any) {
		_ = clipboard.WriteAll(issueKeyFromTuiData(r, d))
	}
}

func renderPlain(w io.Writer, data tui.TableData, delimiter string) error {
	for _, items := range data {
		n := len(items)
		for j, v := range items {
			_, _ = fmt.Fprintf(w, "%s", unescape(v))
			if j != n-1 {
				_, _ = fmt.Fprintf(w, "%s", delimiter)
			}
		}
		_, _ = fmt.Fprintln(w)
	}

	if _, ok := w.(*tabwriter.Writer); ok {
		return w.(*tabwriter.Writer).Flush()
	}
	return nil
}

func renderCSV(w io.Writer, data tui.TableData) error {
	csvwrt := csv.NewWriter(w)

	for _, items := range data {
		if err := csvwrt.Write(items); err != nil {
			return err
		}
	}

	csvwrt.Flush()
	if err := csvwrt.Error(); err != nil {
		return err
	}
	return nil
}

func unescape(s string) string {
	pattern := regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\[\]`)
	return pattern.ReplaceAllString(s, "$1]")
}

func coloredOut(msg string, clr color.Attribute, attrs ...color.Attribute) string {
	c := color.New(clr).Add(attrs...)
	return c.Sprint(msg)
}

func xterm256() bool {
	term := os.Getenv("TERM")
	return strings.Contains(term, "-256color")
}

func gray(msg string) string {
	if xterm256() {
		return gray256(msg)
	}
	return ansi.ColorFunc("black+h")(msg)
}

func gray256(msg string) string {
	return fmt.Sprintf("\x1b[38;5;242m%s\x1b[m", msg)
}

func shortenAndPad(msg string, limit int) string {
	if limit > 1 && len(msg) > limit {
		return msg[0:limit-1] + "…"
	}
	return pad(msg, limit)
}

func pad(msg string, limit int) string {
	var out strings.Builder
	out.WriteString(msg)
	for i := len(msg); i < limit; i++ {
		out.WriteRune(' ')
	}
	return out.String()
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
