package app

import (
	"context"
	"errors"
	"io"
	"os"

	"core/cli/app/commands"
	"core/cli/app/internal/runner"
	"core/shared/config"
	"core/shared/serverapi"

	tea "github.com/charmbracelet/bubbletea"
)

func runUILoopWithInitialPrompt(wiring *runtimeWiring, active config.Settings, logger *runLogger, commandRegistry *commands.Registry, initialPrompt string, initialPromptHistoryRecorded bool, initialInput string, recoveryBuffers []serverapi.SessionDraftRecoveryBuffer, sessionName string, modelContractLocked bool, configuredModelName string, statusConfig uiStatusConfig, startupUpdateNotice bool, markerEncoder runner.TerminalPhaseMarkerEncoder, markerSinkObserver runner.TerminalPhaseMarkerSinkObserver) (tea.Model, error) {
	terminalCursor := newUITerminalCursorState()
	rendererOutputGate := newUIRendererOutputGateState()
	terminalOutput := newUITerminalOutput(os.Stdout, markerEncoder)
	if markerSinkObserver != nil {
		if err := markerSinkObserver.TerminalPhaseMarkerSinkReady(context.Background(), terminalOutput); err != nil {
			return nil, err
		}
	}
	options := mainUIProgramOptionsWithOutput(active, terminalCursor, rendererOutputGate, terminalOutput)
	tuiLogger, err := newRollingTUILogger(statusConfig.PersistenceRoot)
	if err != nil && logger != nil {
		logger.Logf("tui_log.open err=%q", err.Error())
	}
	if tuiLogger != nil {
		defer tuiLogger.Close()
	}
	uiLogger := newMultiUILogger(logger, tuiLogger)
	runtimeClient := wiring.runtimeClient
	if runtimeClient == nil {
		return nil, errors.New("runtime client is required")
	}
	runtimeEvents := wiring.runtimeEvents
	if runtimeEvents == nil {
		return nil, errors.New("runtime event stream is required")
	}
	askEvents := wiring.askEvents
	if askEvents == nil {
		return nil, errors.New("prompt event stream is required")
	}
	sessionID := ""
	if runtimeClient != nil {
		sessionID = runtimeClient.MainView().Session.SessionID
	}

	model := NewProjectedUIModel(
		runtimeClient,
		runtimeEvents,
		askEvents,
		WithUILogger(uiLogger),
		WithUIModelName(active.Model),
		WithUIConfiguredModelName(configuredModelName),
		WithUIThinkingLevel(active.ThinkingLevel),
		WithUIModelContractLocked(modelContractLocked),
		WithUITheme(active.Theme),
		WithUIDebug(active.Debug),
		WithUICommandRegistry(commandRegistry),
		WithUIHasOtherSessions(wiring.hasOtherSessionsKnown, wiring.hasOtherSessions),
		WithUITurnQueueHook(wiring.turnQueueHook),
		WithUIProcessClient(newUIProcessClientWithReads(wiring.processViews, wiring.processControls)),
		WithUIWorktreeClient(wiring.worktrees),
		WithUIPromptHistory(wiring.promptHistory),
		WithUIStartupSubmit(initialPrompt),
		WithUIStartupSubmitPromptHistoryRecorded(initialPromptHistoryRecorded),
		WithUIInitialInput(initialInput),
		WithUIInitialRecoveryBuffers(recoveryBuffers),
		WithUISessionName(sessionName),
		WithUISessionID(sessionID),
		WithUIStatusConfig(statusConfig),
		WithUIStartupUpdateNotice(startupUpdateNotice),
		WithUITerminalCursorState(terminalCursor),
		WithUIRendererOutputGateState(rendererOutputGate),
		WithUITerminalFocusState(wiring.terminalFocus),
	)
	if closable, ok := model.(interface{ Close() }); ok {
		defer closable.Close()
	}
	program := tea.NewProgram(model, options...)

	finalModel, runErr := program.Run()
	if runErr != nil {
		if uiLogger != nil {
			uiLogger.Logf("app.exit err=%q", runErr.Error())
		}
		return nil, runErr
	}
	if uiLogger != nil {
		uiLogger.Logf("app.exit ok")
	}
	return finalModel, nil
}

func mainUIProgramOptionsWithOutput(active config.Settings, terminalCursor *uiTerminalCursorState, rendererOutputGate *uiRendererOutputGateState, output io.Writer) []tea.ProgramOption {
	options := []tea.ProgramOption{tea.WithFilter(terminalCursorProgramFilter(terminalCursor)), tea.WithReportFocus()}
	rendererOutput := output
	if terminalCursor != nil {
		rendererOutput = newUITerminalCursorWriter(rendererOutput, terminalCursor)
	}
	if rendererOutputGate != nil {
		rendererOutput = newUIRendererOutputGateWriter(rendererOutput, rendererOutputGate)
	}
	if rendererOutput != nil && rendererOutput != output {
		options = append(options, tea.WithOutput(rendererOutput))
	}
	return options
}

func extractUITransition(model tea.Model) UITransition {
	if model == nil {
		return UITransition{Action: UIActionNone}
	}
	typed, ok := model.(*uiModel)
	if !ok {
		return UITransition{Action: UIActionNone}
	}
	return typed.Transition()
}
