package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"golang.design/x/clipboard"

	"github.com/charmbracelet/bubbles/help"
	"github.com/charmbracelet/bubbles/key"
	"github.com/charmbracelet/bubbles/textarea"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
)

var (
	cursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212"))

	cursorLineStyle = lipgloss.NewStyle().
			Background(lipgloss.Color("57")).
			Foreground(lipgloss.Color("230"))

	placeholderStyle = lipgloss.NewStyle().
				Foreground(lipgloss.Color("238"))

	endOfBufferStyle = lipgloss.NewStyle().
				Foreground(lipgloss.Color("235"))

	focusedPlaceholderStyle = lipgloss.NewStyle().
				Foreground(lipgloss.Color("99"))

	focusedBorderStyle = lipgloss.NewStyle().
				Border(lipgloss.RoundedBorder()).
				BorderForeground(lipgloss.Color("238"))

	blurredBorderStyle = lipgloss.NewStyle().
				Border(lipgloss.HiddenBorder())
)

type keymap = struct {
	copy, clear, quit key.Binding
}

func newTextarea() textarea.Model {
	t := textarea.New()
	t.Prompt = ""
	t.Placeholder = "Type something"
	t.ShowLineNumbers = true
	t.Cursor.Style = cursorStyle
	t.FocusedStyle.Placeholder = focusedPlaceholderStyle
	t.BlurredStyle.Placeholder = placeholderStyle
	t.FocusedStyle.CursorLine = cursorLineStyle
	t.FocusedStyle.Base = focusedBorderStyle
	t.BlurredStyle.Base = blurredBorderStyle
	t.FocusedStyle.EndOfBuffer = endOfBufferStyle
	t.BlurredStyle.EndOfBuffer = endOfBufferStyle
	t.KeyMap.DeleteWordBackward.SetEnabled(false)
	t.KeyMap.LineNext = key.NewBinding(key.WithKeys("down"))
	t.KeyMap.LinePrevious = key.NewBinding(key.WithKeys("up"))
	t.CharLimit = (1024 * 8) * 8
	t.MaxHeight = 1024 * 8
	t.Blur()
	return t
}

type model struct {
	width  int
	height int
	keymap keymap
	help   help.Model
	out    textarea.Model
}

func newModel() model {
	m := model{
		out:  newTextarea(),
		help: help.New(),
		keymap: keymap{
			quit: key.NewBinding(
				key.WithKeys("esc", "ctrl+c"),
				key.WithHelp("esc", "quit"),
			),
			copy: key.NewBinding(
				key.WithKeys("ctrl+x"),
				key.WithHelp("ctrl+x", "copy"),
			),
			clear: key.NewBinding(
				key.WithKeys("ctrl+r"),
				key.WithHelp("ctrl+r", "clear"),
			),
		},
	}
	m.out.Focus()
	return m
}

func (m model) Init() tea.Cmd {
	return textarea.Blink
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmds []tea.Cmd

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch {
		case key.Matches(msg, m.keymap.quit):
			m.out.Blur()
			return m, tea.Quit
		case key.Matches(msg, m.keymap.copy):
			clipboard.Write(clipboard.FmtText, []byte(m.out.Value()))
		case key.Matches(msg, m.keymap.clear):
			m.out.SetValue("")
			lines = lines[:0]
		}
	case tea.WindowSizeMsg:
		m.height = msg.Height
		m.width = msg.Width
	}

	m.sizeInputs()
	encoded, cmd := m.out.Update(msg)
	cmds = append(cmds, cmd)
	m.out = encoded
	return m, tea.Batch(cmds...)
}

func (m *model) sizeInputs() {
	m.out.SetHeight(m.height - 5)
	m.out.SetWidth(m.width - 5)
}

var lines []string
func (m model) View() string {
	help := m.help.ShortHelpView([]key.Binding{
		m.keymap.copy,
		m.keymap.quit,
	})

	var views []string
	m.out.SetValue(strings.Join(lines, "\n"))
	views = append(views, m.out.View())

	return lipgloss.JoinHorizontal(lipgloss.Top, views...) + "\n" + help
}

var Error *log.Logger
var Warning *log.Logger

func init() {
	log.SetFlags(log.Lmicroseconds | log.Lshortfile)
	logFile, err := os.Create("main.log")
	if err != nil {
		log.Printf("Error creating log file: %v", err)
		os.Exit(1)
	}
	logger := io.MultiWriter(logFile)
	log.SetOutput(logger)

	Error = log.New(io.MultiWriter(logFile, os.Stderr),
		fmt.Sprintf("%sERROR:%s ", "\033[0;101m", "\033[0m"),
		log.Lmicroseconds|log.Lshortfile)
	Warning = log.New(io.MultiWriter(logFile),
		fmt.Sprintf("%sWarning:%s ", "\033[0;93m", "\033[0m"),
		log.Lmicroseconds|log.Lshortfile)
}

func main() {
	err := clipboard.Init()
	if err != nil {
		panic(err)
	}
	
	go func() {
		ch := clipboard.Watch(context.TODO(), clipboard.FmtText)
		for data := range ch {
			// log.Printf("%#v", m.out.Value() + "\n" + string(data))
			// m.out.SetValue(m.out.Value() + "\n" + string(data))
			lines = append(lines, string(data))
		}
	}()

	if _, err := tea.NewProgram(newModel(), tea.WithAltScreen()).Run(); err != nil {
		fmt.Println("Error while running program:", err)
		os.Exit(1)
	}
}
