package api

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"log"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"

	"github.com/oracle/oci-go-sdk/v65/common"
	"gorm.io/gorm"
	_ "oci-portal/internal/model" // swagger 注解引用

	"oci-portal/internal/oci"
	"oci-portal/internal/service"
)

type ociConfigHandler struct {
	svc *service.OciConfigService
}

// importRequest 中 configIni 与显式字段二选一，私钥内容必填。
type importRequest struct {
	Alias            string `json:"alias" binding:"required"`
	Group            string `json:"group"` // 面板内自定义分组，可空
	ConfigINI        string `json:"configIni"`
	TenancyOCID      string `json:"tenancyOcid"`
	UserOCID         string `json:"userOcid"`
	Region           string `json:"region"`
	Fingerprint      string `json:"fingerprint"`
	PrivateKey       string `json:"privateKey" binding:"required"`
	Passphrase       string `json:"passphrase"`
	MultiRegion      bool   `json:"multiRegion"`      // 开启后订阅区域入库缓存
	MultiCompartment bool   `json:"multiCompartment"` // 开启后 compartment 入库缓存
	ProxyID          *uint  `json:"proxyId"`          // 关联出站代理,null 直连
}

// @Summary  导入租户配置
// @Tags     租户配置
// @Param    body body importRequest true "API Key 配置(私钥密文落库)"
// @Success  201 {object} model.OciConfig
// @Security BearerAuth
// @Router   /api/v1/oci-configs [post]
func (h *ociConfigHandler) create(c *gin.Context) {
	var req importRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	cfg, err := h.svc.Import(c.Request.Context(), service.ImportInput{
		Alias:            req.Alias,
		Group:            req.Group,
		ConfigINI:        req.ConfigINI,
		TenancyOCID:      req.TenancyOCID,
		UserOCID:         req.UserOCID,
		Region:           req.Region,
		Fingerprint:      req.Fingerprint,
		PrivateKey:       req.PrivateKey,
		Passphrase:       req.Passphrase,
		MultiRegion:      req.MultiRegion,
		MultiCompartment: req.MultiCompartment,
		ProxyID:          req.ProxyID,
	})
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, cfg)
}

// @Summary  租户配置列表
// @Tags     租户配置
// @Success  200 {array} service.ConfigSummary
// @Security BearerAuth
// @Router   /api/v1/oci-configs [get]
func (h *ociConfigHandler) list(c *gin.Context) {
	items, err := h.svc.List(c.Request.Context())
	if err != nil {
		respondError(c, err)
		return
	}
	c.JSON(http.StatusOK, items)
}

// @Summary  租户配置详情
// @Tags     租户配置
// @Param    id path int true "配置 ID"
// @Success  200 {object} model.OciConfig
// @Security BearerAuth
// @Router   /api/v1/oci-configs/{id} [get]
func (h *ociConfigHandler) get(c *gin.Context) {
	id, ok := pathID(c)
	if !ok {
		return
	}
	cfg, err := h.svc.Get(c.Request.Context(), id)
	if err != nil {
		respondError(c, err)
		return
	}
	c.JSON(http.StatusOK, cfg)
}

// @Summary  验证配置连通性并刷新画像
// @Tags     租户配置
// @Param    id path int true "配置 ID"
// @Success  200 {object} configChangesResponse "config 与 changes 字段变更对照"
// @Security BearerAuth
// @Router   /api/v1/oci-configs/{id}/verify [post]
func (h *ociConfigHandler) verify(c *gin.Context) {
	id, ok := pathID(c)
	if !ok {
		return
	}
	cfg, changes, err := h.svc.Verify(c.Request.Context(), id)
	if err != nil {
		respondError(c, err)
		return
	}
	c.JSON(http.StatusOK, gin.H{"config": cfg, "changes": changes})
}

// updateConfigRequest 空字段不变更；替换私钥须同时给新指纹；
// passphrase / group 一经提供整体覆盖（空串分别表示清除口令 / 取消分组）。
type updateConfigRequest struct {
	Alias            string  `json:"alias"`
	Group            *string `json:"group"`
	Region           string  `json:"region"`
	Fingerprint      string  `json:"fingerprint"`
	PrivateKey       string  `json:"privateKey"`
	Passphrase       *string `json:"passphrase"`
	MultiRegion      *bool   `json:"multiRegion"`      // 非 null 时切换多区域支持
	MultiCompartment *bool   `json:"multiCompartment"` // 非 null 时切换多区间支持
	ProxyID          *uint   `json:"proxyId"`          // 非 null 切换代理:0 解除,>0 关联
}

// @Summary  更新租户配置
// @Tags     租户配置
// @Param    id path int true "配置 ID"
// @Param    body body object true "可更新字段(别名/分组/代理/多区域开关等)"
// @Success  200 {object} configChangesResponse "config 与 changes 字段变更对照"
// @Security BearerAuth
// @Router   /api/v1/oci-configs/{id} [put]
func (h *ociConfigHandler) update(c *gin.Context) {
	id, ok := pathID(c)
	if !ok {
		return
	}
	var req updateConfigRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	cfg, changes, err := h.svc.Update(c.Request.Context(), id, service.UpdateInput{
		Alias:            req.Alias,
		Group:            req.Group,
		Region:           req.Region,
		Fingerprint:      req.Fingerprint,
		PrivateKey:       req.PrivateKey,
		Passphrase:       req.Passphrase,
		MultiRegion:      req.MultiRegion,
		MultiCompartment: req.MultiCompartment,
		ProxyID:          req.ProxyID,
	})
	if err != nil {
		respondError(c, err)
		return
	}
	c.JSON(http.StatusOK, gin.H{"config": cfg, "changes": changes})
}

// @Summary  删除租户配置
// @Description 删除租户配置及其本地关联任务、快照、回传、告警和 AI 渠道数据，并撤销 Webhook 密钥；不删除 OCI 云端资源。
// @Tags     租户配置
// @Param    id path int true "配置 ID"
// @Success  204 "无内容"
// @Security BearerAuth
// @Router   /api/v1/oci-configs/{id} [delete]
func (h *ociConfigHandler) remove(c *gin.Context) {
	id, ok := pathID(c)
	if !ok {
		return
	}
	if err := h.svc.Delete(c.Request.Context(), id); err != nil {
		respondError(c, err)
		return
	}
	c.Status(http.StatusNoContent)
}

// compartments 列出租户下全部 ACTIVE compartment（不含租户根）。
//
// @Summary  列出租户下全部 ACTIVE compartment
// @Tags     租户配置
// @Param    id path int true "配置 ID"
// @Success  200 {array} oci.Compartment
// @Security BearerAuth
// @Router   /api/v1/oci-configs/{id}/compartments [get]
func (h *ociConfigHandler) compartments(c *gin.Context) {
	id, ok := pathID(c)
	if !ok {
		return
	}
	items, err := h.svc.Compartments(c.Request.Context(), id)
	if err != nil {
		respondError(c, err)
		return
	}
	c.JSON(http.StatusOK, items)
}

// cachedRegions 返回筛选器用区域列表：未开多区域支持只含默认区域；
// 缓存存在非 READY 时实时刷新。
//
// @Summary  返回筛选器用区域列表：未开多区域支持只含默认区域
// @Tags     租户配置
// @Param    id path int true "配置 ID"
// @Success  200 {array} service.RegionSubscriptionView
// @Security BearerAuth
// @Router   /api/v1/oci-configs/{id}/cached-regions [get]
func (h *ociConfigHandler) cachedRegions(c *gin.Context) {
	id, ok := pathID(c)
	if !ok {
		return
	}
	items, err := h.svc.CachedRegions(c.Request.Context(), id)
	if err != nil {
		respondError(c, err)
		return
	}
	c.JSON(http.StatusOK, items)
}

// cachedCompartments 返回筛选器用区间列表：未开多区间支持返回空数组。
//
// @Summary  返回筛选器用区间列表：未开多区间支持返回空数组
// @Tags     租户配置
// @Param    id path int true "配置 ID"
// @Success  200 {array} oci.Compartment
// @Security BearerAuth
// @Router   /api/v1/oci-configs/{id}/cached-compartments [get]
func (h *ociConfigHandler) cachedCompartments(c *gin.Context) {
	id, ok := pathID(c)
	if !ok {
		return
	}
	items, err := h.svc.CachedCompartments(c.Request.Context(), id)
	if err != nil {
		respondError(c, err)
		return
	}
	c.JSON(http.StatusOK, items)
}

func pathID(c *gin.Context) (uint, bool) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
		return 0, false
	}
	return uint(id), true
}

// respondError 统一出错响应边界(S-08):OCI 服务错误经脱敏透出,记录缺失
// 映射 404;其余按内部错误处理——响应只含固定文案与关联 ID,完整原因
// (可能携带 SQL / DSN / 路径等内部细节)仅写服务端日志,经同一 ID 对应。
func respondError(c *gin.Context, err error) {
	var svcErr common.ServiceError
	if errors.As(err, &svcErr) {
		respondOCIError(c, err, svcErr)
		return
	}
	if errors.Is(err, gorm.ErrRecordNotFound) {
		c.JSON(http.StatusNotFound, gin.H{"error": "资源不存在"})
		return
	}
	id := newRequestID()
	log.Printf("[ERR %s] %s %s: %v", id, c.Request.Method, requestPath(c), err)
	c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误", "requestId": id})
}

// newRequestID 生成错误关联 ID(8 字节随机 hex),响应与服务端日志据此对应。
func newRequestID() string {
	b := make([]byte, 8)
	_, _ = rand.Read(b)
	return hex.EncodeToString(b)
}

// respondOCIError 提炼 OCI 服务错误：透传 HTTP 状态码，
// error 只保留操作前缀 + 错误码 + 服务端消息（oci.CompactError）。
func respondOCIError(c *gin.Context, err error, svcErr common.ServiceError) {
	status := svcErr.GetHTTPStatusCode()
	if status < http.StatusBadRequest {
		status = http.StatusBadGateway
	}
	// 面板的 401 专属本地 JWT 失效（前端收到即登出）、429 专属本地 IP 限速
	// （前端收到即跳 /blocked）；上游 OCI 的同码是代理调用被拒/云端限流，
	// 均改用 502 透出，body 保留 ociCode 供辨识
	if status == http.StatusUnauthorized || status == http.StatusTooManyRequests {
		status = http.StatusBadGateway
	}
	body := gin.H{
		"error":        oci.CompactError(err),
		"ociCode":      svcErr.GetCode(),
		"opcRequestId": svcErr.GetOpcRequestID(),
	}
	if hint := oci.ErrorHint(err); hint != "" {
		body["hint"] = hint
	}
	c.JSON(status, body)
}
