// Package terminal owns interactive confirmation, notices, and Bubble Tea launch.
package terminal

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"albedo/cli/internal/app"
	"albedo/cli/internal/daemon"
	"albedo/cli/internal/tui"

	tea "charm.land/bubbletea/v2"
	"github.com/charmbracelet/x/ansi"
	"github.com/mattn/go-isatty"
)

type Service struct {
	In          io.Reader
	Out, Err    io.Writer
	OpenBrowser func(string)
	Home        string
	Capable     bool
}

func IsTTY(in io.Reader, out io.Writer) bool {
	input, inputOK := in.(*os.File)
	output, outputOK := out.(*os.File)
	return inputOK && outputOK && isatty.IsTerminal(input.Fd()) && isatty.IsTerminal(output.Fd())
}

// announceMigration tells the user, once, that the daemon's start moved their
// secrets into creds.json, and waits for enter so the TUI does not cover it.
// The daemon answers only the first client that asks.
func (t *Service) announceMigration(ctx context.Context, conn *daemon.Connection) error {
	moved, err := daemon.TakeMigration(ctx, conn)
	if err != nil || len(moved) == 0 {
		return nil
	}
	backups := filepath.Join(t.Home, "backups", "*-before-creds-*")
	if _, writeErr := fmt.Fprintf(t.Out, "Your credentials have been moved from %s to %s.\n", strings.Join(moved, ", "), filepath.Join(t.Home, "creds.json")); writeErr != nil {
		return writeErr
	}
	if _, writeErr := fmt.Fprintf(t.Out, "The backups still contain your credentials. Once you have checked that login works,\nyou can delete those backups with:\n\n  rm -f %s\n\nPress Enter to continue. ", backups); writeErr != nil {
		return writeErr
	}
	_, err = bufio.NewReader(t.In).ReadString('\n')
	if errors.Is(err, io.EOF) {
		return nil
	}
	return err
}

// Confirmed reads a [y/N] answer. A late reply to the terminal's startup
// queries can precede it.
func Confirmed(answer string) bool {
	switch strings.ToLower(strings.TrimSpace(ansi.Strip(answer))) {
	case "y", "yes":
		return true
	}
	return false
}

func (t *Service) Open(ctx context.Context, prepared app.PreparedOpen) error {
	if err := t.announceMigration(ctx, prepared.Connection); err != nil {
		return err
	}
	if err := tui.DetectInk(); err != nil {
		return err
	}
	appModel := tui.NewAppModel(prepared.Connection, prepared.Providers, prepared.Selected, prepared.Workspace, prepared.LoginRequired, t.OpenBrowser)
	p := tea.NewProgram(appModel, tea.WithFPS(120), tea.WithInput(t.In), tea.WithOutput(t.Out))
	final, err := p.Run()
	if err != nil {
		return err
	}
	if m, ok := final.(tui.AppModel); ok && m.ActiveSession != nil {
		_, err = fmt.Fprintf(t.Out, "To reopen this session, run albedo resume %s\n", m.ActiveSession.ID)
	}
	return err
}

func (t *Service) Login(ctx context.Context, prepared app.PreparedOpen, workspace, name string) error {
	if err := t.announceMigration(ctx, prepared.Connection); err != nil {
		return err
	}
	if err := tui.DetectInk(); err != nil {
		return err
	}
	model := tui.NewLoginAppModel(prepared.Connection, prepared.Providers, workspace, name, t.OpenBrowser)
	_, err := tea.NewProgram(model, tea.WithFPS(120), tea.WithInput(t.In), tea.WithOutput(t.Out)).Run()
	return err
}
func (t *Service) ConfirmCleanup(sessions bool) (bool, error) {
	if !t.Capable {
		return false, errors.New("no files have been removed; run cleanup in a terminal to confirm, or add --yes to approve the changes shown above")
	}
	prompt := "Apply the cleanup shown above? [y/N] "
	if sessions {
		prompt = "Permanently delete the sessions listed above? [y/N] "
	}
	if _, err := fmt.Fprint(t.Out, prompt); err != nil {
		return false, err
	}
	answer, err := bufio.NewReader(t.In).ReadString('\n')
	if err != nil && !errors.Is(err, io.EOF) {
		return false, err
	}
	return Confirmed(answer), nil
}
