package progress

import (
	"context"
	"fmt"
	"github.com/Azure/aztfexport/pkg/meta"

	"github.com/Azure/aztfexport/internal/ui/aztfexportclient"
	"github.com/Azure/aztfexport/internal/ui/common"
	prog "github.com/charmbracelet/bubbles/progress"
	tea "github.com/charmbracelet/bubbletea"
)

type result struct {
	item  meta.ImportItem
	emoji string
}

type Model struct {
	ctx context.Context
	c   meta.Meta
	l   meta.ImportList

	idx         int
	parallelism int

	results  []result
	progress prog.Model
}

func NewModel(ctx context.Context, c meta.Meta, parallelism int, l meta.ImportList) Model {
	return Model{
		ctx:         ctx,
		c:           c,
		l:           l,
		idx:         0,
		parallelism: parallelism,
		results:     make([]result, common.ProgressShowLastResults),
		progress:    prog.NewModel(prog.WithDefaultGradient()),
	}
}

func (m Model) Init() tea.Cmd {
	if m.iterationDone() {
		return aztfexportclient.FinishImport(m.l)
	}

	n := m.parallelism
	if m.idx+m.parallelism > len(m.l) {
		n = len(m.l) - m.idx
	}
	return tea.Batch(
		aztfexportclient.ImportItems(m.ctx, m.c, m.l[m.idx:m.idx+n]),
	)
}

func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		m.progress.Width = msg.Width - 4
		return m, nil
	// FrameMsg is sent when the progress bar wants to animate itself
	case prog.FrameMsg:
		progressModel, cmd := m.progress.Update(msg)
		m.progress = progressModel.(prog.Model)
		return m, cmd

	case aztfexportclient.ImportItemsDoneMsg:
		var cmds []tea.Cmd

		// Update results
		items := msg.Items
		for i := range items {
			m.l[m.idx+i] = items[i]

			emoji := common.RandomHappyEmoji()
			if items[i].ImportError != nil {
				emoji = common.WarningEmoji
			}
			res := result{
				item:  items[i],
				emoji: emoji,
			}
			m.results = append(m.results[1:], res)
		}

		m.idx += m.parallelism

		if m.iterationDone() {
			cmds = append(cmds, m.progress.SetPercent(1))
			cmds = append(cmds, aztfexportclient.FinishImport(m.l))
			return m, tea.Batch(cmds...)
		}

		cmds = append(cmds, m.progress.SetPercent(float64(m.idx)/float64(len(m.l))))

		n := m.parallelism
		if m.idx+m.parallelism > len(m.l) {
			n = len(m.l) - m.idx
		}
		cmds = append(cmds, aztfexportclient.ImportItems(m.ctx, m.c, m.l[m.idx:m.idx+n]))
		return m, tea.Batch(cmds...)

	default:
		return m, nil
	}
}

func (m Model) View() string {
	msg := ""
	if len(m.l) > m.idx {
		item := m.l[m.idx]
		if item.Skip() {
			msg = fmt.Sprintf(" Skipping %s...", item.TFResourceId)
		} else {
			msg = fmt.Sprintf(" Importing %s...", item.TFResourceId)
		}
	}

	s := fmt.Sprintf(" %s\n\n", msg)
	for _, res := range m.results {
		// This indicates the state before the item is inserted as the to results.
		if res.item.TFResourceId == "" {
			s += "...\n"
		} else {
			switch {
			case res.item.Skip():
				s += fmt.Sprintf("%s %s skipped\n", res.emoji, res.item.TFResourceId)
			default:
				if res.item.ImportError == nil {
					s += fmt.Sprintf("%s %s import successfully\n", res.emoji, res.item.TFResourceId)
				} else {
					s += fmt.Sprintf("%s %s import failed\n", res.emoji, res.item.TFResourceId)
				}
			}
		}
	}

	s += "\n\n" + m.progress.View()

	return s
}

func (m Model) iterationDone() bool {
	return m.idx >= len(m.l)
}
