package protocol

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/steveyegge/gastown/internal/mail"
	"github.com/steveyegge/gastown/internal/mrqueue"
)

// DefaultRefineryHandler provides the default implementation for Refinery protocol handlers.
// It receives MERGE_READY messages from the Witness and adds work to the merge queue.
type DefaultRefineryHandler struct {
	// Rig is the name of the rig this refinery processes.
	Rig string

	// WorkDir is the working directory for operations.
	WorkDir string

	// Queue is the merge request queue.
	Queue *mrqueue.Queue

	// Router is used to send mail messages.
	Router *mail.Router

	// Output is where to write status messages.
	Output io.Writer
}

// NewRefineryHandler creates a new DefaultRefineryHandler.
func NewRefineryHandler(rig, workDir string) *DefaultRefineryHandler {
	return &DefaultRefineryHandler{
		Rig:     rig,
		WorkDir: workDir,
		Queue:   mrqueue.New(workDir),
		Router:  mail.NewRouter(workDir),
		Output:  os.Stdout,
	}
}

// SetOutput sets the output writer for status messages.
func (h *DefaultRefineryHandler) SetOutput(w io.Writer) {
	h.Output = w
}

// HandleMergeReady handles a MERGE_READY message from Witness.
// When a polecat's work is verified and ready, the Refinery:
// 1. Validates the merge request
// 2. Adds it to the merge queue
// 3. Acknowledges receipt
func (h *DefaultRefineryHandler) HandleMergeReady(payload *MergeReadyPayload) error {
	_, _ = fmt.Fprintf(h.Output, "[Refinery] MERGE_READY received for polecat %s\n", payload.Polecat)
	_, _ = fmt.Fprintf(h.Output, "  Branch: %s\n", payload.Branch)
	_, _ = fmt.Fprintf(h.Output, "  Issue: %s\n", payload.Issue)
	_, _ = fmt.Fprintf(h.Output, "  Verified: %s\n", payload.Verified)

	// Validate required fields
	if payload.Branch == "" {
		return fmt.Errorf("missing branch in MERGE_READY payload")
	}
	if payload.Polecat == "" {
		return fmt.Errorf("missing polecat in MERGE_READY payload")
	}

	// Create merge request (ID is generated by Submit if empty)
	mr := &mrqueue.MR{
		Branch:      payload.Branch,
		Worker:      payload.Polecat,
		SourceIssue: payload.Issue,
		Target:      "main", // Default target, could be passed in payload
		Rig:         payload.Rig,
		Title:       fmt.Sprintf("Merge %s work on %s", payload.Polecat, payload.Issue),
		CreatedAt:   time.Now(),
	}

	// Add to queue
	if err := h.Queue.Submit(mr); err != nil {
		_, _ = fmt.Fprintf(h.Output, "[Refinery] Error adding to queue: %v\n", err)
		return fmt.Errorf("failed to add merge request to queue: %w", err)
	}

	_, _ = fmt.Fprintf(h.Output, "[Refinery] ✓ Added to merge queue: %s\n", mr.ID)
	_, _ = fmt.Fprintf(h.Output, "  Queue length: %d\n", h.Queue.Count())

	return nil
}

// SendMerged sends a MERGED message to the Witness.
// Called by the Refinery after successfully merging a branch.
func (h *DefaultRefineryHandler) SendMerged(polecat, branch, issue, targetBranch, mergeCommit string) error {
	msg := NewMergedMessage(h.Rig, polecat, branch, issue, targetBranch, mergeCommit)
	return h.Router.Send(msg)
}

// SendMergeFailed sends a MERGE_FAILED message to the Witness.
// Called by the Refinery when a merge fails.
func (h *DefaultRefineryHandler) SendMergeFailed(polecat, branch, issue, targetBranch, failureType, errorMsg string) error {
	msg := NewMergeFailedMessage(h.Rig, polecat, branch, issue, targetBranch, failureType, errorMsg)
	return h.Router.Send(msg)
}

// SendReworkRequest sends a REWORK_REQUEST message to the Witness.
// Called by the Refinery when a branch has conflicts.
func (h *DefaultRefineryHandler) SendReworkRequest(polecat, branch, issue, targetBranch string, conflictFiles []string) error {
	msg := NewReworkRequestMessage(h.Rig, polecat, branch, issue, targetBranch, conflictFiles)
	return h.Router.Send(msg)
}

// NotifyMergeOutcome is a convenience method that sends the appropriate message
// based on the merge result.
type MergeOutcome struct {
	// Success indicates whether the merge was successful.
	Success bool

	// Conflict indicates the failure was due to conflicts (needs rebase).
	Conflict bool

	// FailureType categorizes the failure (e.g., "tests", "build").
	FailureType string

	// Error is the error message if the merge failed.
	Error string

	// MergeCommit is the SHA of the merge commit on success.
	MergeCommit string

	// ConflictFiles lists files with conflicts (if Conflict is true).
	ConflictFiles []string
}

// NotifyMergeOutcome sends the appropriate protocol message based on the outcome.
func (h *DefaultRefineryHandler) NotifyMergeOutcome(polecat, branch, issue, targetBranch string, outcome MergeOutcome) error {
	if outcome.Success {
		return h.SendMerged(polecat, branch, issue, targetBranch, outcome.MergeCommit)
	}

	if outcome.Conflict {
		return h.SendReworkRequest(polecat, branch, issue, targetBranch, outcome.ConflictFiles)
	}

	return h.SendMergeFailed(polecat, branch, issue, targetBranch, outcome.FailureType, outcome.Error)
}

// Ensure DefaultRefineryHandler implements RefineryHandler.
var _ RefineryHandler = (*DefaultRefineryHandler)(nil)
