package countryregion

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/config"
	"hyapp-admin-server/internal/integration/roomclient"
	"hyapp-admin-server/internal/integration/userclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/repository"
	"hyapp-admin-server/internal/response"

	"github.com/gin-gonic/gin"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type Handler struct {
	cfg     config.Config
	service *Service
	audit   shared.OperationLogger
}

func New(userClient userclient.Client, userDB *sql.DB, jobStore *repository.Store, roomClient roomclient.Client, cfg config.Config, audit shared.OperationLogger) *Handler {
	return &Handler{cfg: cfg, service: NewService(userClient, userDB, jobStore, roomClient), audit: audit}
}

func (h *Handler) Service() *Service {
	if h == nil {
		return nil
	}
	return h.service
}

// ListCountries 返回国家主数据列表；enabled 不传时返回全部国家。
func (h *Handler) ListCountries(c *gin.Context) {
	enabled, ok := parseOptionalBoolQuery(c, "enabled")
	if !ok {
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	items, err := h.service.ListCountries(ctx, middleware.CurrentRequestID(c), enabled)
	if err != nil {
		writeCountryRegionError(c, err, "获取国家列表失败")
		return
	}
	response.OK(c, gin.H{"items": items, "total": len(items)})
}

// GetCountry 返回单个国家；user-service 没有单条 RPC 时由 service 复用列表做轻量过滤。
func (h *Handler) GetCountry(c *gin.Context) {
	countryID, ok := parseInt64ID(c, "country_id")
	if !ok {
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	country, err := h.service.GetCountry(ctx, middleware.CurrentRequestID(c), countryID)
	if err != nil {
		writeCountryRegionError(c, err, "获取国家失败")
		return
	}
	response.OK(c, country)
}

// CreateCountry 创建国家；这是后台管理后端能力，countryCode 创建后不可变。
func (h *Handler) CreateCountry(c *gin.Context) {
	var req createCountryRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "国家参数不正确")
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	country, err := h.service.CreateCountry(ctx, actorID(c), middleware.CurrentRequestID(c), req)
	if err != nil {
		writeCountryRegionError(c, err, "创建国家失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "create-country", "countries", country.CountryID,
		fmt.Sprintf("country_id=%d country_code=%s enabled=%t", country.CountryID, country.CountryCode, country.Enabled))
	response.Created(c, country)
}

// UpdateCountry 修改国家展示和辅助字段；不会转发 countryCode，避免后台误改主键语义。
func (h *Handler) UpdateCountry(c *gin.Context) {
	countryID, ok := parseInt64ID(c, "country_id")
	if !ok {
		return
	}

	var req updateCountryRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "国家参数不正确")
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	country, err := h.service.UpdateCountry(ctx, actorID(c), countryID, middleware.CurrentRequestID(c), req)
	if err != nil {
		writeCountryRegionError(c, err, "更新国家失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "update-country", "countries", country.CountryID,
		fmt.Sprintf("country_id=%d country_code=%s", country.CountryID, country.CountryCode))
	response.OK(c, country)
}

// RenameCountryCode 创建国家码重命名后台任务；实际改码由 admin_jobs runner 串行执行并同步下游当前投影。
func (h *Handler) RenameCountryCode(c *gin.Context) {
	countryID, ok := parseInt64ID(c, "country_id")
	if !ok {
		return
	}

	var req renameCountryCodeRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "国家参数不正确")
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	actor := shared.ActorFromContext(c)
	job, err := h.service.RenameCountryCode(ctx, int64(actor.UserID), actor.Username, countryID, middleware.CurrentRequestID(c), req)
	if err != nil {
		writeCountryRegionError(c, err, "创建国家码重命名任务失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "rename-country-code", "countries", countryID,
		fmt.Sprintf("country_id=%d old_country_code=%s new_country_code=%s job_id=%d", job.CountryID, job.OldCountryCode, job.NewCountryCode, job.JobID))
	response.Created(c, job)
}

// EnableCountry 重新开放国家给 App 注册和用户改国家入口。
func (h *Handler) EnableCountry(c *gin.Context) {
	countryID, ok := parseInt64ID(c, "country_id")
	if !ok {
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	country, err := h.service.EnableCountry(ctx, actorID(c), countryID, middleware.CurrentRequestID(c))
	if err != nil {
		writeCountryRegionError(c, err, "启用国家失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "enable-country", "countries", country.CountryID,
		fmt.Sprintf("country_id=%d country_code=%s", country.CountryID, country.CountryCode))
	response.OK(c, country)
}

// DisableCountry 是国家“删除”的实际业务动作：软禁用 App 可选状态，不硬删历史主数据。
func (h *Handler) DisableCountry(c *gin.Context) {
	countryID, ok := parseInt64ID(c, "country_id")
	if !ok {
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	country, err := h.service.DisableCountry(ctx, actorID(c), countryID, middleware.CurrentRequestID(c))
	if err != nil {
		writeCountryRegionError(c, err, "禁用国家失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "disable-country", "countries", country.CountryID,
		fmt.Sprintf("country_id=%d country_code=%s", country.CountryID, country.CountryCode))
	response.OK(c, country)
}

// ListRegions 返回区域列表；status 为空时返回全部，active/disabled 由 user-service 校验。
func (h *Handler) ListRegions(c *gin.Context) {
	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	items, err := h.service.ListRegions(ctx, middleware.CurrentRequestID(c), c.Query("status"))
	if err != nil {
		writeCountryRegionError(c, err, "获取区域列表失败")
		return
	}
	response.OK(c, gin.H{"items": items, "total": len(items)})
}

// GetRegion 返回单个区域及其当前 active 国家码列表。
func (h *Handler) GetRegion(c *gin.Context) {
	regionID, ok := parseInt64ID(c, "region_id")
	if !ok {
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	region, err := h.service.GetRegion(ctx, middleware.CurrentRequestID(c), regionID)
	if err != nil {
		writeCountryRegionError(c, err, "获取区域失败")
		return
	}
	response.OK(c, region)
}

// CreateRegion 创建区域并设置首版国家归属。
func (h *Handler) CreateRegion(c *gin.Context) {
	var req createRegionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "区域参数不正确")
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	region, err := h.service.CreateRegion(ctx, actorID(c), middleware.CurrentRequestID(c), req)
	if err != nil {
		writeCountryRegionError(c, err, "创建区域失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "create-region", "regions", region.RegionID,
		fmt.Sprintf("region_id=%d region_code=%s countries=%d", region.RegionID, region.RegionCode, len(region.Countries)))
	response.Created(c, region)
}

// UpdateRegion 修改区域展示字段，不修改 countries。
func (h *Handler) UpdateRegion(c *gin.Context) {
	regionID, ok := parseInt64ID(c, "region_id")
	if !ok {
		return
	}

	var req updateRegionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "区域参数不正确")
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	region, err := h.service.UpdateRegion(ctx, actorID(c), regionID, middleware.CurrentRequestID(c), req)
	if err != nil {
		writeCountryRegionError(c, err, "更新区域失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "update-region", "regions", region.RegionID,
		fmt.Sprintf("region_id=%d region_code=%s", region.RegionID, region.RegionCode))
	response.OK(c, region)
}

// ReplaceRegionCountries 替换区域国家归属，并触发 user-service 的用户区域重算任务。
func (h *Handler) ReplaceRegionCountries(c *gin.Context) {
	regionID, ok := parseInt64ID(c, "region_id")
	if !ok {
		return
	}

	var req replaceRegionCountriesRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "区域国家参数不正确")
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	region, err := h.service.ReplaceRegionCountries(ctx, actorID(c), regionID, middleware.CurrentRequestID(c), req)
	if err != nil {
		writeCountryRegionError(c, err, "更新区域国家失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "replace-region-countries", "regions", region.RegionID,
		fmt.Sprintf("region_id=%d region_code=%s countries=%d", region.RegionID, region.RegionCode, len(region.Countries)))
	response.OK(c, region)
}

// EnableRegion 重新启用区域主数据。
func (h *Handler) EnableRegion(c *gin.Context) {
	regionID, ok := parseInt64ID(c, "region_id")
	if !ok {
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	region, err := h.service.EnableRegion(ctx, actorID(c), regionID, middleware.CurrentRequestID(c))
	if err != nil {
		writeCountryRegionError(c, err, "启用区域失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "enable-region", "regions", region.RegionID,
		fmt.Sprintf("region_id=%d region_code=%s", region.RegionID, region.RegionCode))
	response.OK(c, region)
}

// DisableRegion 是区域“删除”的实际业务动作：停用区域并释放 active 国家归属。
func (h *Handler) DisableRegion(c *gin.Context) {
	regionID, ok := parseInt64ID(c, "region_id")
	if !ok {
		return
	}

	ctx, cancel := h.userServiceContext(c)
	defer cancel()

	region, err := h.service.DisableRegion(ctx, actorID(c), regionID, middleware.CurrentRequestID(c))
	if err != nil {
		writeCountryRegionError(c, err, "停用区域失败")
		return
	}
	writeCountryRegionAuditLog(c, h.audit, "disable-region", "regions", region.RegionID,
		fmt.Sprintf("region_id=%d region_code=%s", region.RegionID, region.RegionCode))
	response.OK(c, region)
}

func (h *Handler) userServiceContext(c *gin.Context) (context.Context, context.CancelFunc) {
	return context.WithTimeout(c.Request.Context(), h.cfg.UserService.RequestTimeout)
}

func actorID(c *gin.Context) int64 {
	return int64(shared.ActorFromContext(c).UserID)
}

func parseInt64ID(c *gin.Context, name string) (int64, bool) {
	id, ok := shared.ParseID(c, name)
	if !ok {
		return 0, false
	}
	return int64(id), true
}

func parseOptionalBoolQuery(c *gin.Context, name string) (*bool, bool) {
	raw := strings.TrimSpace(c.Query(name))
	if raw == "" {
		return nil, true
	}
	value, err := strconv.ParseBool(raw)
	if err != nil {
		response.BadRequest(c, name+" 参数不正确")
		return nil, false
	}
	return &value, true
}

func writeCountryRegionError(c *gin.Context, err error, fallback string) {
	if errors.Is(err, errCountryNotFound) {
		response.NotFound(c, "国家不存在")
		return
	}
	if errors.Is(err, errCountryInvalid) {
		response.BadRequest(c, "国家参数不正确")
		return
	}
	if errors.Is(err, errCountryConflict) {
		response.BadRequest(c, "国家已存在")
		return
	}
	if errors.Is(err, errCountryRenameJobConflict) {
		response.BadRequest(c, "国家码重命名任务正在处理中")
		return
	}
	if errors.Is(err, errRegionNotFound) {
		response.NotFound(c, "区域不存在")
		return
	}
	if errors.Is(err, errRegionInvalid) {
		response.BadRequest(c, "区域参数不正确")
		return
	}
	if errors.Is(err, errRegionConflict) {
		response.BadRequest(c, "区域已存在")
		return
	}
	if errors.Is(err, errRegionCountryMapping) {
		response.BadRequest(c, "国家已归属其他启用区域")
		return
	}
	if errors.Is(err, errGlobalRegionProtected) {
		response.BadRequest(c, "GLOBAL 区域不能停用或配置国家")
		return
	}
	if st, ok := status.FromError(err); ok {
		switch st.Code() {
		case codes.InvalidArgument, codes.AlreadyExists, codes.FailedPrecondition, codes.Aborted:
			response.BadRequest(c, grpcMessage(st, fallback))
		case codes.NotFound:
			response.NotFound(c, grpcMessage(st, fallback))
		default:
			response.ServerError(c, fallback)
		}
		return
	}
	response.ServerError(c, fallback)
}

func grpcMessage(st *status.Status, fallback string) string {
	if message := strings.TrimSpace(st.Message()); message != "" {
		return message
	}
	return fallback
}

func writeCountryRegionAuditLog(c *gin.Context, logger shared.OperationLogger, action string, resource string, resourceID int64, detail string) {
	// 国家/区域的后台审计只写 admin-server 自己的 admin_operation_logs。
	// user-service 仍只保存国家/区域主数据事实和其自身领域审计。
	shared.OperationLogWithResourceID(c, logger, action, resource, fmt.Sprintf("%d", resourceID), "success", detail)
}
