// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package outputs

import (
	"fmt"
	"math"
	"strings"

	ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
	"github.com/charmbracelet/bubbles/viewport"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
)

const (
	// verbose view formatting.
	outlinePadding = 8

	// controls.
	verboseControls = "Controls: ↑/↓ - up/down • esc - return to table • q - quit"
)

// verboseModel represents the current state of the verbose view.
type verboseModel struct {
	// model for verbose output viewport
	viewport viewport.Model

	// the instance which the verbose output is focused on
	focusedInstanceName ec2types.InstanceType
}

// styling for viewport.
var (
	titleStyle = func() lipgloss.Style {
		b := lipgloss.RoundedBorder()
		b.Right = "├"
		return lipgloss.NewStyle().BorderStyle(b).Padding(0, 1)
	}()

	infoStyle = func() lipgloss.Style {
		b := lipgloss.RoundedBorder()
		b.Left = "┤"
		return titleStyle.BorderStyle(b)
	}()
)

// initVerboseModel initializes and returns a new verboseModel based on the given
// instance type details.
func initVerboseModel() *verboseModel {
	viewportModel := viewport.New(initialDimensionVal, initialDimensionVal)
	viewportModel.MouseWheelEnabled = true

	return &verboseModel{
		viewport: viewportModel,
	}
}

// resizeView will change the dimensions of the verbose viewport in order to accommodate
// the new window dimensions represented by the given tea.WindowSizeMsg.
func (m verboseModel) resizeView(msg tea.WindowSizeMsg) verboseModel {
	// handle width changes
	m.viewport.Width = msg.Width

	// handle height changes
	if outlinePadding >= msg.Height {
		// height too short to fit viewport
		m.viewport.Height = 0
	} else {
		newHeight := msg.Height - outlinePadding
		m.viewport.Height = newHeight
	}

	return m
}

// update updates the state of the verboseModel.
func (m verboseModel) update(msg tea.Msg) (verboseModel, tea.Cmd) {
	var cmd tea.Cmd
	m.viewport, cmd = m.viewport.Update(msg)
	return m, cmd
}

func (m verboseModel) view() string {
	outputStr := strings.Builder{}

	// format header for viewport
	instanceName := titleStyle.Render(string(m.focusedInstanceName))
	line := strings.Repeat("─", int(math.Max(0, float64(m.viewport.Width-lipgloss.Width(instanceName)))))
	outputStr.WriteString(lipgloss.JoinHorizontal(lipgloss.Center, instanceName, line))
	outputStr.WriteString("\n")

	outputStr.WriteString(m.viewport.View())
	outputStr.WriteString("\n")

	// format footer for viewport
	pagePercentage := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100))
	line = strings.Repeat("─", int(math.Max(0, float64(m.viewport.Width-lipgloss.Width(pagePercentage)))))
	outputStr.WriteString(lipgloss.JoinHorizontal(lipgloss.Center, line, pagePercentage))
	outputStr.WriteString("\n")

	// controls
	outputStr.WriteString(controlsStyle.Render(verboseControls))
	outputStr.WriteString("\n")

	return outputStr.String()
}
