package run

import (
	"context"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"

	ws "github.com/coder/websocket"
	"github.com/coder/websocket/wsjson"

	"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
	httpx "github.com/yan1h/agent-coding-workflow/internal/transport/http"
	"github.com/yan1h/agent-coding-workflow/internal/transport/http/middleware"
	"github.com/yan1h/agent-coding-workflow/internal/workspace"
)

const defaultLogsLimit = 500

// AdminLookup resolves whether a user is an admin（mirrors acp/workspace pattern）。
type AdminLookup interface {
	IsAdmin(ctx context.Context, userID uuid.UUID) (bool, error)
}

// WSConfig controls WebSocket heartbeat and buffering for the log stream.
type WSConfig struct {
	PingInterval time.Duration
	PongTimeout  time.Duration
	SendBuffer   int
}

// Handler 暴露 run profiles 的 HTTP/WS 端点。
type Handler struct {
	svc         *Service
	resolver    middleware.SessionResolver
	adminLookup AdminLookup
	cfg         WSConfig
}

// NewHandler 构造 run.Handler。
func NewHandler(svc *Service, resolver middleware.SessionResolver, al AdminLookup, cfg WSConfig) *Handler {
	return &Handler{svc: svc, resolver: resolver, adminLookup: al, cfg: cfg}
}

// Mount 注册 run profiles 路由。分两组：workspace 作用域（列表/创建）与
// id 作用域（按 id 操作，内部经 profile→workspace 解析做 ACL）。
func (h *Handler) Mount(r chi.Router) {
	r.Route("/api/v1/workspaces/{wsID}/run-profiles", func(r chi.Router) {
		r.Use(middleware.Auth(h.resolver, middleware.AuthOptions{}))
		r.Get("/", h.list)
		r.Post("/", h.create)
	})
	r.Route("/api/v1/run-profiles", func(r chi.Router) {
		r.Use(middleware.Auth(h.resolver, middleware.AuthOptions{}))
		r.Patch("/{id}", h.update)
		r.Delete("/{id}", h.remove)
		r.Post("/{id}/start", h.start)
		r.Post("/{id}/stop", h.stop)
		r.Post("/{id}/restart", h.restart)
		r.Get("/{id}/status", h.status)
		r.Get("/{id}/logs", h.logs)
		r.Get("/{id}/logs/stream", h.logsStream)
	})
}

func (h *Handler) caller(r *http.Request) (workspace.Caller, error) {
	uid, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		return workspace.Caller{}, errs.New(errs.CodeUnauthorized, "not authenticated")
	}
	admin, err := h.adminLookup.IsAdmin(r.Context(), uid)
	if err != nil {
		return workspace.Caller{}, err
	}
	return workspace.Caller{UserID: uid, IsAdmin: admin}, nil
}

func uuidParam(r *http.Request, key string) (uuid.UUID, error) {
	id, err := uuid.Parse(chi.URLParam(r, key))
	if err != nil {
		return uuid.Nil, errs.New(errs.CodeInvalidInput, "bad "+key)
	}
	return id, nil
}

func (h *Handler) list(w http.ResponseWriter, r *http.Request) {
	c, err := h.caller(r)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	wsID, err := uuidParam(r, "wsID")
	if err != nil {
		writeErr(w, r, err)
		return
	}
	out, err := h.svc.List(r.Context(), c, wsID)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	httpx.WriteJSON(w, http.StatusOK, out)
}

func (h *Handler) create(w http.ResponseWriter, r *http.Request) {
	c, err := h.caller(r)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	wsID, err := uuidParam(r, "wsID")
	if err != nil {
		writeErr(w, r, err)
		return
	}
	var req CreateRunProfileReq
	if derr := json.NewDecoder(r.Body).Decode(&req); derr != nil {
		writeErr(w, r, errs.New(errs.CodeInvalidInput, "bad json"))
		return
	}
	out, err := h.svc.Create(r.Context(), c, wsID, req)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	httpx.WriteJSON(w, http.StatusCreated, out)
}

func (h *Handler) update(w http.ResponseWriter, r *http.Request) {
	c, err := h.caller(r)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	id, err := uuidParam(r, "id")
	if err != nil {
		writeErr(w, r, err)
		return
	}
	var req UpdateRunProfileReq
	if derr := json.NewDecoder(r.Body).Decode(&req); derr != nil {
		writeErr(w, r, errs.New(errs.CodeInvalidInput, "bad json"))
		return
	}
	out, err := h.svc.Update(r.Context(), c, id, req)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	httpx.WriteJSON(w, http.StatusOK, out)
}

func (h *Handler) remove(w http.ResponseWriter, r *http.Request) {
	c, err := h.caller(r)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	id, err := uuidParam(r, "id")
	if err != nil {
		writeErr(w, r, err)
		return
	}
	if err := h.svc.Delete(r.Context(), c, id); err != nil {
		writeErr(w, r, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func (h *Handler) start(w http.ResponseWriter, r *http.Request)   { h.lifecycle(w, r, h.svc.Start) }
func (h *Handler) stop(w http.ResponseWriter, r *http.Request)    { h.lifecycle(w, r, h.svc.Stop) }
func (h *Handler) restart(w http.ResponseWriter, r *http.Request) { h.lifecycle(w, r, h.svc.Restart) }
func (h *Handler) status(w http.ResponseWriter, r *http.Request)  { h.lifecycle(w, r, h.svc.Status) }

// lifecycle 复用 start/stop/restart/status 的公共骨架（鉴权 + 解析 id + 调 svc + 写 RunStatusResp）。
func (h *Handler) lifecycle(w http.ResponseWriter, r *http.Request,
	fn func(context.Context, workspace.Caller, uuid.UUID) (RunStatusResp, error)) {
	c, err := h.caller(r)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	id, err := uuidParam(r, "id")
	if err != nil {
		writeErr(w, r, err)
		return
	}
	out, err := fn(r.Context(), c, id)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	httpx.WriteJSON(w, http.StatusOK, out)
}

func (h *Handler) logs(w http.ResponseWriter, r *http.Request) {
	c, err := h.caller(r)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	id, err := uuidParam(r, "id")
	if err != nil {
		writeErr(w, r, err)
		return
	}
	since := parseInt64(r.URL.Query().Get("since"), 0)
	limit := int(parseInt64(r.URL.Query().Get("limit"), defaultLogsLimit))
	out, err := h.svc.Logs(r.Context(), c, id, since, limit)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	httpx.WriteJSON(w, http.StatusOK, out)
}

// logsStream 升级为 WebSocket，推送内存日志（含 since 续传）与实时新行。只读流，
// 不接收客户端消息。鉴权经 middleware.Auth（WS 用 ?token= 传 token）。
func (h *Handler) logsStream(w http.ResponseWriter, r *http.Request) {
	c, err := h.caller(r)
	if err != nil {
		writeErr(w, r, err)
		return
	}
	id, err := uuidParam(r, "id")
	if err != nil {
		writeErr(w, r, err)
		return
	}
	since := parseInt64(r.URL.Query().Get("since"), 0)

	sub, relay, running, err := h.svc.SubscribeLogs(r.Context(), c, id, c.UserID)
	if err != nil {
		writeErr(w, r, err)
		return
	}

	conn, aerr := ws.Accept(w, r, &ws.AcceptOptions{})
	if aerr != nil {
		return
	}
	defer conn.Close(ws.StatusInternalError, "internal")

	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()

	// 初始 state 帧。
	status := StatusStopped
	if running {
		status = StatusRunning
	}
	if werr := wsjson.Write(ctx, conn, LogEnvelope{Kind: "state", Status: string(status), TS: time.Now()}); werr != nil {
		return
	}

	// profile 未运行：无 live relay，推完即关。
	if !running || relay == nil {
		conn.Close(ws.StatusNormalClosure, "not running")
		return
	}
	defer relay.Unsubscribe(sub.ID)

	// 推送历史缓冲（ID > since）。
	lastSent := since
	for _, e := range relay.since(since, 0) {
		if werr := wsjson.Write(ctx, conn, e); werr != nil {
			return
		}
		lastSent = e.ID
	}

	// 心跳。
	pingTicker := time.NewTicker(h.cfg.PingInterval)
	defer pingTicker.Stop()
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case <-pingTicker.C:
				pingCtx, pcancel := context.WithTimeout(ctx, h.cfg.PongTimeout)
				perr := conn.Ping(pingCtx)
				pcancel()
				if perr != nil {
					cancel()
					return
				}
			}
		}
	}()

	for {
		select {
		case <-ctx.Done():
			return
		case env, ok := <-sub.Send:
			if !ok {
				conn.Close(ws.StatusNormalClosure, "relay closed")
				return
			}
			// 历史已推到 lastSent，避免重复推送同 ID 的日志行（控制帧 ID=0 放行）。
			if env.Kind == "log" && env.ID <= lastSent && env.ID != 0 {
				continue
			}
			if werr := wsjson.Write(ctx, conn, env); werr != nil {
				return
			}
			if env.ID > lastSent {
				lastSent = env.ID
			}
		}
	}
}

func parseInt64(s string, def int64) int64 {
	if s == "" {
		return def
	}
	n, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return def
	}
	return n
}

func writeErr(w http.ResponseWriter, r *http.Request, err error) {
	httpx.WriteError(w, middleware.RequestIDFromContext(r.Context()), err)
}
