package handler

import (
	"encoding/json"
	"net/http"

	"aijianzhan/platform/internal/authx"
	"aijianzhan/platform/internal/dbsync"
	"aijianzhan/platform/internal/svc"

	"github.com/zeromicro/go-zero/rest/httpx"
	"github.com/zeromicro/go-zero/rest/pathvar"
)

func requireDBSync(svcCtx *svc.ServiceContext, w http.ResponseWriter) bool {
	if svcCtx.DBSync == nil {
		authx.WriteError(w, http.StatusServiceUnavailable, "dbsync not enabled")
		return false
	}
	return true
}

// sync 仅公司顶级权限（管理员 /「数据同步」）；智能体与编辑不可配。
func syncTenantID(r *http.Request) int64 {
	return authx.TenantID(r.Context())
}

func syncListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		list, err := svcCtx.DBSync.Store().ListChannelsByTenant(syncTenantID(r))
		if err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, map[string]any{"items": list})
	}
}

func syncGetHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		ch, err := svcCtx.DBSync.Store().GetChannelForTenant(pathvar.Vars(r)["id"], syncTenantID(r))
		if err != nil {
			authx.WriteError(w, http.StatusNotFound, err.Error())
			return
		}
		httpx.OkJson(w, ch)
	}
}

func syncSaveHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		var ch dbsync.Channel
		if err := json.NewDecoder(r.Body).Decode(&ch); err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		if id := pathvar.Vars(r)["id"]; id != "" {
			ch.ID = id
			existing, err := svcCtx.DBSync.Store().GetChannelForTenant(id, syncTenantID(r))
			if err != nil {
				authx.WriteError(w, http.StatusNotFound, err.Error())
				return
			}
			_ = existing
		}
		// 强制归属当前公司，禁止客户端伪造 tenant_id
		ch.TenantID = syncTenantID(r)
		saved, err := svcCtx.DBSync.Store().SaveChannel(ch)
		if err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, saved)
	}
}

func syncDeleteHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		id := pathvar.Vars(r)["id"]
		if _, err := svcCtx.DBSync.Store().GetChannelForTenant(id, syncTenantID(r)); err != nil {
			authx.WriteError(w, http.StatusNotFound, err.Error())
			return
		}
		svcCtx.DBSync.StopChannel(id)
		if err := svcCtx.DBSync.Store().DeleteChannel(id); err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, map[string]any{"ok": true})
	}
}

func syncTestHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		var body struct {
			Local  *dbsync.Endpoint `json:"local"`
			Remote *dbsync.Endpoint `json:"remote"`
			Side   string           `json:"side"` // local|remote|both
		}
		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		out := map[string]any{}
		side := body.Side
		if side == "" {
			side = "both"
		}
		if (side == "local" || side == "both") && body.Local != nil {
			out["local"] = dbsync.TestEndpoint(r.Context(), *body.Local)
		}
		if (side == "remote" || side == "both") && body.Remote != nil {
			out["remote"] = dbsync.TestEndpoint(r.Context(), *body.Remote)
		}
		httpx.OkJson(w, out)
	}
}

func syncPrepareHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		ch, err := svcCtx.DBSync.Store().GetChannelForTenant(pathvar.Vars(r)["id"], syncTenantID(r))
		if err != nil {
			authx.WriteError(w, http.StatusNotFound, err.Error())
			return
		}
		if err := dbsync.PrepareChannel(r.Context(), ch); err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, map[string]any{"ok": true, "message": "outbox + triggers ready"})
	}
}

func syncStartHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		id := pathvar.Vars(r)["id"]
		ch, err := svcCtx.DBSync.Store().GetChannelForTenant(id, syncTenantID(r))
		if err != nil {
			authx.WriteError(w, http.StatusNotFound, err.Error())
			return
		}
		if err := dbsync.PrepareChannel(r.Context(), ch); err != nil {
			authx.WriteError(w, http.StatusBadRequest, "prepare: "+err.Error())
			return
		}
		if err := svcCtx.DBSync.StartChannel(id); err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, map[string]any{"ok": true, "running": true})
	}
}

func syncStopHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		id := pathvar.Vars(r)["id"]
		if _, err := svcCtx.DBSync.Store().GetChannelForTenant(id, syncTenantID(r)); err != nil {
			authx.WriteError(w, http.StatusNotFound, err.Error())
			return
		}
		svcCtx.DBSync.StopChannel(id)
		httpx.OkJson(w, map[string]any{"ok": true, "running": false})
	}
}

func syncConflictsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		only := r.URL.Query().Get("unresolved") != "0"
		list, err := svcCtx.DBSync.Store().ListConflictsByTenant(syncTenantID(r), only)
		if err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, map[string]any{"items": list})
	}
}

func syncResolveConflictHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		var body struct {
			Resolution string `json:"resolution"` // apply_source | keep_target | discard
		}
		_ = json.NewDecoder(r.Body).Decode(&body)
		if body.Resolution == "" {
			body.Resolution = "discard"
		}
		id := pathvar.Vars(r)["id"]
		if err := svcCtx.DBSync.Store().ResolveConflictForTenant(id, syncTenantID(r), body.Resolution); err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, map[string]any{"ok": true, "resolution": body.Resolution})
	}
}

func syncReconcileHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		ch, err := svcCtx.DBSync.Store().GetChannelForTenant(pathvar.Vars(r)["id"], syncTenantID(r))
		if err != nil {
			authx.WriteError(w, http.StatusNotFound, err.Error())
			return
		}
		res, err := dbsync.ReconcileChannel(r.Context(), ch)
		if err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, res)
	}
}

func syncIngestHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !requireDBSync(svcCtx, w) {
			return
		}
		ch, err := svcCtx.DBSync.Store().GetChannelForTenant(pathvar.Vars(r)["id"], syncTenantID(r))
		if err != nil {
			authx.WriteError(w, http.StatusNotFound, err.Error())
			return
		}
		var body struct {
			Table  string           `json:"table"`
			Source string           `json:"source"` // 如 c / excel / api
			Rows   []map[string]any `json:"rows"`
		}
		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		n, err := dbsync.IngestRows(r.Context(), ch, body.Table, body.Rows, body.Source)
		if err != nil {
			authx.WriteError(w, http.StatusBadRequest, err.Error())
			return
		}
		httpx.OkJson(w, map[string]any{"ok": true, "ingested": n, "hint": "已写入本地并进入 outbox，将同步到线上"})
	}
}
