// Package main provides the stop hook entry point.
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"github.com/lukaszraczylo/claude-mnemonic/pkg/hooks"
)

var debug = os.Getenv("CLAUDE_MNEMONIC_DEBUG") != ""

// Input is the hook input from Claude Code.
type Input struct {
	hooks.BaseInput
	TranscriptPath string `json:"transcript_path"`
	StopHookActive bool   `json:"stop_hook_active"`
}

// TranscriptMessage represents a message in the transcript JSONL file.
type TranscriptMessage struct {
	Message struct {
		Content any    `json:"content"`
		Role    string `json:"role"`
	} `json:"message"`
	Type string `json:"type"` // Can be string or array
}

// extractTextContent extracts text content from message content (handles both string and array formats).
func extractTextContent(content any) string {
	switch v := content.(type) {
	case string:
		return v
	case []interface{}:
		var texts []string
		for _, item := range v {
			if m, ok := item.(map[string]interface{}); ok {
				if m["type"] == "text" {
					if text, ok := m["text"].(string); ok {
						texts = append(texts, text)
					}
				}
			}
		}
		return strings.Join(texts, "\n")
	}
	return ""
}

// parseTranscript reads the transcript file and extracts the last user and assistant messages.
func parseTranscript(path string) (lastUser, lastAssistant string) {
	// Expand ~ to home directory
	if strings.HasPrefix(path, "~") {
		home, err := os.UserHomeDir()
		if err == nil {
			path = strings.Replace(path, "~", home, 1)
		}
	}

	file, err := os.Open(path) // #nosec G304 -- path is from internal conversation file location
	if err != nil {
		return "", ""
	}
	defer func() { _ = file.Close() }()

	// For large transcripts, seek to the last 256KB for efficiency.
	// We only need the last user/assistant messages, not the entire history.
	const tailSize = 256 * 1024
	info, err := file.Stat()
	if err == nil && info.Size() > tailSize {
		if _, seekErr := file.Seek(-tailSize, io.SeekEnd); seekErr == nil {
			// Discard partial first line after seek
			discardScanner := bufio.NewScanner(file)
			discardScanner.Scan()
		}
	}

	scanner := bufio.NewScanner(file)
	// Increase buffer size for large messages
	buf := make([]byte, 0, 64*1024)
	scanner.Buffer(buf, 1024*1024)

	for scanner.Scan() {
		var msg TranscriptMessage
		if err := json.Unmarshal(scanner.Bytes(), &msg); err != nil {
			continue
		}

		// Transcript entries have type: "user" or "assistant" (not "message")
		// Check if this is a user/assistant message with content
		if msg.Type == "user" || msg.Type == "assistant" {
			text := extractTextContent(msg.Message.Content)
			if text == "" {
				continue
			}

			// Use the outer type field for role (message.role may differ)
			switch msg.Type {
			case "user":
				lastUser = text
			case "assistant":
				lastAssistant = text
			}
		}
	}

	return lastUser, lastAssistant
}

func main() {
	if !hooks.IsWorkerAvailable() {
		hooks.WriteResponse("Stop", true)
		return
	}
	hooks.RunHook("Stop", handleStop)
}

func handleStop(ctx *hooks.HookContext, input *Input) (string, error) {
	deadline, cancel := hooks.HookDeadline(30 * time.Second)
	defer cancel()

	if debug {
		fmt.Fprintf(os.Stderr, "[stop] Raw input: %s\n", string(ctx.RawInput))
	}

	// Find session
	result, err := hooks.GET(ctx.Port, fmt.Sprintf("/api/sessions?claudeSessionId=%s", ctx.SessionID))
	if err != nil || result == nil {
		// Session might not exist, that's OK
		return "", nil
	}

	sessionID, ok := result["id"].(float64)
	if !ok {
		return "", nil
	}

	// Parse transcript to get last messages for summary context
	lastUser, lastAssistant := "", ""
	if input.TranscriptPath != "" {
		lastUser, lastAssistant = parseTranscript(input.TranscriptPath)
	}

	// Truncate messages to avoid sending excessive data to the worker
	if len(lastAssistant) > 10000 {
		lastAssistant = lastAssistant[:10000]
	}
	if len(lastUser) > 5000 {
		lastUser = lastUser[:5000]
	}

	if debug {
		fmt.Fprintf(os.Stderr, "[stop] Transcript path: %s\n", input.TranscriptPath)
		fmt.Fprintf(os.Stderr, "[stop] Last user message length: %d\n", len(lastUser))
		fmt.Fprintf(os.Stderr, "[stop] Last assistant message length: %d\n", len(lastAssistant))
		if len(lastAssistant) > 0 {
			preview := lastAssistant
			if len(preview) > 300 {
				preview = preview[:300] + "..."
			}
			fmt.Fprintf(os.Stderr, "[stop] Last assistant preview: %s\n", preview)
		}
		fmt.Fprintf(os.Stderr, "[stop] Requesting summary for session %d (transcript: %v)\n", int64(sessionID), input.TranscriptPath != "")
	}

	// Check deadline before expensive summary request
	if deadline.Err() != nil {
		fmt.Fprintf(os.Stderr, "[stop] Returning early due to time limit\n")
		return "", nil
	}

	// Request summary with message context from transcript
	_, err = hooks.POST(ctx.Port, fmt.Sprintf("/sessions/%d/summarize", int64(sessionID)), map[string]interface{}{
		"lastUserMessage":      lastUser,
		"lastAssistantMessage": lastAssistant,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "[stop] Warning: summary request failed: %v\n", err)
	}

	return "", nil
}
