// km_writer — 讀寫 Mira leo-graph（journals + pages）
// 透過 host function 呼叫 Mira /km/* API
//
//go:build tinygo

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"unsafe"
)

//go:wasmimport u6u http_request
func hostHttpRequest(
	urlPtr uintptr, urlLen uint32,
	methodPtr uintptr, methodLen uint32,
	headersPtr uintptr, headersLen uint32,
	bodyPtr uintptr, bodyLen uint32,
	outPtr uintptr, outLenPtr uintptr,
) uint32

// Input actions:
//   read_journal        — GET today's journal (requires: mira_url, token)
//   read_journal_date   — GET journal by date (requires: mira_url, token, date)
//   append_journal      — POST append entry (requires: mira_url, token, content; optional: timestamp)
//   list_pages          — GET all pages (requires: mira_url, token)
//   read_page           — GET page by name (requires: mira_url, token, name)
//   write_page          — PUT write page (requires: mira_url, token, name, content)

type Input struct {
	Action    string `json:"action"`
	MiraURL   string `json:"mira_url"`
	Token     string `json:"token"`
	Content   string `json:"content"`
	Timestamp string `json:"timestamp"`
	Date      string `json:"date"`
	Name      string `json:"name"`
}

func main() {
	raw, err := io.ReadAll(os.Stdin)
	if err != nil {
		writeError("failed to read stdin: " + err.Error())
		return
	}
	var inp Input
	if err := json.Unmarshal(raw, &inp); err != nil {
		writeError("invalid input JSON: " + err.Error())
		return
	}
	if inp.Action == "" {
		writeError("action 必填")
		return
	}
	if inp.MiraURL == "" {
		writeError("mira_url 必填")
		return
	}
	if inp.Token == "" {
		writeError("token 必填")
		return
	}

	authHeader := fmt.Sprintf(`{"Authorization":"Bearer %s","Content-Type":"application/json"}`, inp.Token)

	switch inp.Action {
	case "read_journal":
		result := doRequest(inp.MiraURL+"/km/journal", "GET", authHeader, "")
		os.Stdout.Write(result)

	case "read_journal_date":
		if inp.Date == "" {
			writeError("date 必填（格式 YYYY-MM-DD）")
			return
		}
		result := doRequest(inp.MiraURL+"/km/journal/"+inp.Date, "GET", authHeader, "")
		os.Stdout.Write(result)

	case "append_journal":
		if inp.Content == "" {
			writeError("content 必填")
			return
		}
		bodyMap := map[string]string{"content": inp.Content}
		if inp.Timestamp != "" {
			bodyMap["timestamp"] = inp.Timestamp
		}
		bodyBytes, _ := json.Marshal(bodyMap)
		result := doRequest(inp.MiraURL+"/km/journal", "POST", authHeader, string(bodyBytes))
		os.Stdout.Write(result)

	case "list_pages":
		result := doRequest(inp.MiraURL+"/km/pages", "GET", authHeader, "")
		os.Stdout.Write(result)

	case "read_page":
		if inp.Name == "" {
			writeError("name 必填")
			return
		}
		result := doRequest(inp.MiraURL+"/km/page/"+inp.Name, "GET", authHeader, "")
		os.Stdout.Write(result)

	case "write_page":
		if inp.Name == "" {
			writeError("name 必填")
			return
		}
		if inp.Content == "" {
			writeError("content 必填")
			return
		}
		bodyMap := map[string]string{"content": inp.Content}
		bodyBytes, _ := json.Marshal(bodyMap)
		result := doRequest(inp.MiraURL+"/km/page/"+inp.Name, "PUT", authHeader, string(bodyBytes))
		os.Stdout.Write(result)

	default:
		writeError("未知 action: " + inp.Action)
	}
}

func doRequest(url, method, headersJSON, body string) []byte {
	urlBytes := []byte(url)
	methodBytes := []byte(method)
	headersBytes := []byte(headersJSON)
	bodyBytes := []byte(body)

	outBuf := make([]byte, 131072) // 128KB
	var outLen uint32

	if len(bodyBytes) == 0 {
		bodyBytes = []byte{}
	}

	var bodyPtr uintptr
	var bodyLen uint32
	if len(bodyBytes) > 0 {
		bodyPtr = uintptr(unsafe.Pointer(&bodyBytes[0]))
		bodyLen = uint32(len(bodyBytes))
	}

	code := hostHttpRequest(
		uintptr(unsafe.Pointer(&urlBytes[0])), uint32(len(urlBytes)),
		uintptr(unsafe.Pointer(&methodBytes[0])), uint32(len(methodBytes)),
		uintptr(unsafe.Pointer(&headersBytes[0])), uint32(len(headersBytes)),
		bodyPtr, bodyLen,
		uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)),
	)

	if code != 0 {
		out, _ := json.Marshal(map[string]interface{}{"success": false, "error": "HTTP request failed"})
		return out
	}

	responseStr := string(outBuf[:outLen])

	// Try to parse the response as JSON to forward it
	var parsed interface{}
	if err := json.Unmarshal([]byte(responseStr), &parsed); err != nil {
		// Not JSON — wrap it
		out, _ := json.Marshal(map[string]interface{}{"success": true, "data": responseStr})
		return out
	}

	// Forward the parsed response as-is, wrapped in success
	out, _ := json.Marshal(map[string]interface{}{"success": true, "data": parsed})
	return out
}

func writeError(msg string) {
	out, _ := json.Marshal(map[string]interface{}{"success": false, "error": msg})
	os.Stdout.Write(out)
}
